Dopo aver risolto questo problema nei miei sistemi, ecco un metodo per determinare correttamente il valore di default di un tipo arbitrario in fase di esecuzione, che è stato testato contro le migliaia di tipi:
/// <summary>
/// [ <c>public static object GetDefault(this Type type)</c> ]
/// <para></para>
/// Retrieves the default value for a given Type
/// </summary>
/// <param name="type">The Type for which to get the default value</param>
/// <returns>The default value for <paramref name="type"/></returns>
/// <remarks>
/// If a null Type, a reference Type, or a System.Void Type is supplied, this method always returns null. If a value type
/// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an
/// exception.
/// </remarks>
/// <example>
/// To use this method in its native, non-extension form, make a call like:
/// <code>
/// object Default = DefaultValue.GetDefault(someType);
/// </code>
/// To use this method in its Type-extension form, make a call like:
/// <code>
/// object Default = someType.GetDefault();
/// </code>
/// </example>
/// <seealso cref="GetDefault<T>"/>
public static object GetDefault(this Type type)
{
// If no Type was supplied, if the Type was a reference type, or if the Type was a System.Void, return null
if (type == null || !type.IsValueType || type == typeof(void))
return null;
// If the supplied Type has generic parameters, its default value cannot be determined
if (type.ContainsGenericParameters)
throw new ArgumentException(
"{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type +
"> contains generic parameters, so the default value cannot be retrieved");
// If the Type is a primitive type, or if it is another publicly-visible value type (i.e. struct/enum), return a
// default instance of the value type
if (type.IsPrimitive || !type.IsNotPublic)
{
try
{
return Activator.CreateInstance(type);
}
catch (Exception e)
{
throw new ArgumentException(
"{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe Activator.CreateInstance method could not " +
"create a default instance of the supplied value type <" + type +
"> (Inner Exception message: \"" + e.Message + "\")", e);
}
}
// Fail with exception
throw new ArgumentException("{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type +
"> is not a publicly-visible type, so the default value cannot be retrieved");
}
In questi esempi, il metodo GetDefault è implementato nella classe statica DefaultValue. Chiamare questo metodo con una dichiarazione come:
object Default = DefaultValue.GetDefault(someType);
Per utilizzare il metodo getDefault come un metodo di estensione per il tipo, lo chiamano così:
object Default = someType.GetDefault();
Questo secondo approccio di tipo estensione è un client semplice -codice di codice, poiché rimuove la necessità di fare riferimento al qualificatore di classe DefaultValue che contiene la chiamata.
La precedente forma di esecuzione di GetDefault funziona con la semantica identica come la parola chiave di default C# "predefinita" e produce gli stessi risultati.
Per utilizzare una forma generica di getDefault, è possibile accedere alle seguenti funzioni:
/// <summary>
/// [ <c>public static T GetDefault< T >()</c> ]
/// <para></para>
/// Retrieves the default value for a given Type
/// </summary>
/// <typeparam name="T">The Type for which to get the default value</typeparam>
/// <returns>The default value for Type T</returns>
/// <remarks>
/// If a reference Type or a System.Void Type is supplied, this method always returns null. If a value type
/// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an
/// exception.
/// </remarks>
/// <seealso cref="GetDefault(Type)"/>
public static T GetDefault<T>()
{
return (T) GetDefault(typeof(T));
}
Una chiamata alla forma generica potrebbe essere qualcosa di simile:
int? inDefaultVal = DefaultValue.GetDefault<int?>();
Naturalmente, quanto sopra generica la forma di GetDefault non è necessaria per C#, poiché funziona allo stesso modo di default (T). È utile solo per un linguaggio .NET che non supporta la parola chiave 'default' ma che supporta tipi generici. Nella maggior parte dei casi, la forma generica non è necessaria.
Un metodo corollario utile è uno per determinare se un oggetto contiene il valore predefinito per il suo Tipo. Ho anche contare sulla seguente metodo IsObjectSetToDefault a tal fine:
/// <summary>
/// [ <c>public static bool IsObjectSetToDefault(this Type ObjectType, object ObjectValue)</c> ]
/// <para></para>
/// Reports whether a value of type T (or a null reference of type T) contains the default value for that Type
/// </summary>
/// <remarks>
/// Reports whether the object is empty or unitialized for a reference type or nullable value type (i.e. is null) or
/// whether the object contains a default value for a non-nullable value type (i.e. int = 0, bool = false, etc.)
/// <para></para>
/// NOTE: For non-nullable value types, this method introduces a boxing/unboxing performance penalty.
/// </remarks>
/// <param name="ObjectType">Type of the object to test</param>
/// <param name="ObjectValue">The object value to test, or null for a reference Type or nullable value Type</param>
/// <returns>
/// true = The object contains the default value for its Type.
/// <para></para>
/// false = The object has been changed from its default value.
/// </returns>
public static bool IsObjectSetToDefault(this Type ObjectType, object ObjectValue)
{
// If no ObjectType was supplied, attempt to determine from ObjectValue
if (ObjectType == null)
{
// If no ObjectValue was supplied, abort
if (ObjectValue == null)
{
MethodBase currmethod = MethodInfo.GetCurrentMethod();
string ExceptionMsgPrefix = currmethod.DeclaringType + " {" + currmethod + "} Error:\n\n";
throw new ArgumentNullException(ExceptionMsgPrefix + "Cannot determine the ObjectType from a null Value");
}
// Determine ObjectType from ObjectValue
ObjectType = ObjectValue.GetType();
}
// Get the default value of type ObjectType
object Default = ObjectType.GetDefault();
// If a non-null ObjectValue was supplied, compare Value with its default value and return the result
if (ObjectValue != null)
return ObjectValue.Equals(Default);
// Since a null ObjectValue was supplied, report whether its default value is null
return Default == null;
}
Il metodo sopra IsObjectSetToDefault
può o essere richiamato nella sua forma nativa o accessibile come un'estensione di tipo classe.
Puoi spiegare perché non funziona? Ricevi un errore? Non restituisce semplicemente ciò che ti aspetti? – Gabe
@Josh, grazie! ti è piaciuto. – viky
@gabe, Funziona con il nome del tipo, ma non con l'istanza Type del nome di quel tipo, voglio dire default (Decimal) funziona ma default (typeof (Decimal)) non è – viky