Valor predeterminado de un tipo en Runtime

Para cualquier tipo dado, quiero saber su valor predeterminado.

En C #, hay una palabra clave llamada predeterminada para hacer esto, como

object obj = default(Decimal); 

pero tengo una instancia de Type (llamada myType) y si digo esto,

 object obj = default(myType); 

no funciona

¿Hay alguna buena manera de hacer esto? Sé que un bloque de interruptores enorme funcionará, pero no es una buena opción.

En realidad, solo hay dos posibilidades: null para los tipos de referencia y new myType() para los tipos de valor (que corresponde a 0 para int, float, etc.). De modo que solo necesita dar cuenta de dos casos:

 object GetDefaultValue(Type t) { if (t.IsValueType) return Activator.CreateInstance(t); return null; } 

(Debido a que los tipos de valor siempre tienen un constructor predeterminado, esa llamada a Activator.CreateInstance nunca fallará).

También puede agregarlo como método de extensión a System.Type:

 public static class TypeExtensions { public static object GetDefaultValue(this Type t) { if (t.IsValueType && Nullable.GetUnderlyingType(t) == null) return Activator.CreateInstance(t); else return null; } } 

Habiendo resuelto este problema en mis propios sistemas, aquí hay un método para determinar correctamente el valor predeterminado de un tipo arbitrario en tiempo de ejecución, que se ha probado contra miles de tipos:

  ///  /// [ public static object GetDefault(this Type type) ] ///  /// Retrieves the default value for a given Type ///  /// The Type for which to get the default value /// The default value for  ///  /// 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. ///  ///  /// To use this method in its native, non-extension form, make a call like: ///  /// object Default = DefaultValue.GetDefault(someType); ///  /// To use this method in its Type-extension form, make a call like: ///  /// object Default = someType.GetDefault(); ///  ///  ///  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 (ie 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"); } 

En estos ejemplos, el método GetDefault se implementa en la clase estática DefaultValue. Llame a este método con una statement como:

  object Default = DefaultValue.GetDefault(someType); 

Para usar el método GetDefault como método de extensión para Type, llámalo así:

  object Default = someType.GetDefault(); 

Este segundo enfoque de extensión de tipo es una syntax de código de cliente más simple, ya que elimina la necesidad de hacer referencia al calificador de clase DefaultValue que contiene la llamada.

La forma de tiempo de ejecución anterior de GetDefault funciona con semántica idéntica a la palabra clave primitiva C # ‘default’, y produce los mismos resultados.

Para usar una forma genérica de GetDefault, puede acceder a la siguiente función:

  ///  /// [ public static T GetDefault< T >() ] ///  /// Retrieves the default value for a given Type ///  /// The Type for which to get the default value /// The default value for Type T ///  /// 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. ///  ///  public static T GetDefault() { return (T) GetDefault(typeof(T)); } 

Una llamada a la forma genérica podría ser algo así como:

  int? inDefaultVal = DefaultValue.GetDefault(); 

Por supuesto, la forma genérica de GetDefault anterior no es necesaria para C #, ya que funciona igual que la predeterminada (T). Solo es útil para un lenguaje .NET que no admite la palabra clave ‘predeterminada’ pero que admite tipos generics. En la mayoría de los casos, la forma genérica es innecesaria.

Un método de corolario útil es uno para determinar si un objeto contiene el valor predeterminado para su Tipo. También confío en el siguiente método IsObjectSetToDefault para ese propósito:

  ///  /// [ public static bool IsObjectSetToDefault(this Type ObjectType, object ObjectValue) ] ///  /// Reports whether a value of type T (or a null reference of type T) contains the default value for that Type ///  ///  /// Reports whether the object is empty or unitialized for a reference type or nullable value type (ie is null) or /// whether the object contains a default value for a non-nullable value type (ie int = 0, bool = false, etc.) ///  /// NOTE: For non-nullable value types, this method introduces a boxing/unboxing performance penalty. ///  /// Type of the object to test /// The object value to test, or null for a reference Type or nullable value Type ///  /// true = The object contains the default value for its Type. ///  /// false = The object has been changed from its default value. ///  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; } 

El método anterior IsObjectSetToDefault se puede llamar en su forma nativa o se puede acceder a él como una extensión de clase de tipo.

¿Qué tal algo así como …

 class Program { static void Main(string[] args) { PrintDefault(typeof(object)); PrintDefault(typeof(string)); PrintDefault(typeof(int)); PrintDefault(typeof(int?)); } private static void PrintDefault(Type type) { Console.WriteLine("default({0}) = {1}", type, DefaultGenerator.GetDefaultValue(type)); } } public class DefaultGenerator { public static object GetDefaultValue(Type parameter) { var defaultGeneratorType = typeof(DefaultGenerator<>).MakeGenericType(parameter); return defaultGeneratorType.InvokeMember( "GetDefault", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, new object[0]); } } public class DefaultGenerator { public static T GetDefault() { return default(T); } } 

Produce el siguiente resultado:

 default(System.Object) = default(System.String) = default(System.Int32) = 0 default(System.Nullable`1[System.Int32]) = 

¿Qué quiere decir con “Valor predeterminado”? Todos los tipos de referencia (“clase”) tienen nulo como valor predeterminado, mientras que todos los tipos de valores tendrán sus valores predeterminados de acuerdo con esta tabla .

Aquí hay una función que devolverá el valor predeterminado para un tipo que admite nulos (en otras palabras, devuelve 0 tanto para Decimal como para Decimal? ):

 public static object DefaultValue(Type maybeNullable) { Type underlying = Nullable.GetUnderlyingType(maybeNullable); if (underlying != null) return Activator.CreateInstance(underlying); return Activator.CreateInstance(maybeNullable); }