valores - tipos de datos en c# visual studio
Valor predeterminado de un tipo en Runtime (6)
¿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 .
Esta pregunta ya tiene una respuesta aquí:
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.
¿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<T>
{
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]) =
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);
}
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á).
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:
/// <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");
}
En estos ejemplos, el método GetDefault se implementa en la clase estática DefaultValue. Llame a este método con una declaración 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 sintaxis 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:
/// <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 llamada a la forma genérica podría ser algo así como:
int? inDefaultVal = DefaultValue.GetDefault<int?>();
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 genéricos. 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:
/// <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;
}
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.
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;
}
}