我想知道任何给定类型的默认值。
在C#中,有一个关键字叫做default可以用于获取它的默认值,就像这样:
object obj = default(Decimal);
但是我有一个 Type 实例(称为 myType),如果我这样说:
object obj = default(myType);
它不起作用
有没有什么好方法可以做到这一点? 我知道使用一个巨大的 switch 块可以工作,但那不是一个好的选择。
我想知道任何给定类型的默认值。
在C#中,有一个关键字叫做default可以用于获取它的默认值,就像这样:
object obj = default(Decimal);
但是我有一个 Type 实例(称为 myType),如果我这样说:
object obj = default(myType);
它不起作用
有没有什么好方法可以做到这一点? 我知道使用一个巨大的 switch 块可以工作,但那不是一个好的选择。
实际上只有两种可能性:null
代表引用类型,new myType()
代表值类型(对应 int、float 等的值为0)。因此,您只需要考虑这两种情况:
object GetDefaultValue(Type t)
{
if (t.IsValueType)
return Activator.CreateInstance(t);
return null;
}
string
失败,而default
运算符不会。 - Shimmy WeitzhandlerActivator.CreateInstance
总是返回一个System.Object
对象。因此,如果要求它创建一个ValueType
类型的实例,则必须将其装箱。Nullable<T>
在运行时中有特别处理装箱操作的机制,只返回T
而不包含Nullable
的包装器。但是,当HasValue
为false时,该object
引用就是简单的null
值。因此,从例如Activator.CreateInstance(typeof(int?))
中获取的确实是一个真正的null
引用,而不是一个int?
。可能会有一个内部的int?
,但它在装箱过程中消失了。 - Jonathan Gilbertpublic static class TypeExtensions
{
public static object GetDefaultValue(this Type t)
{
if (t.IsValueType && Nullable.GetUnderlyingType(t) == null)
return Activator.CreateInstance(t);
else
return null;
}
}
Nullable.GetUnderlyingType(t) == null
是用来做什么的?在什么情况下它可能不为null? - Muflix在我的系统中已经解决了这个问题,以下是一种在运行时正确确定任意类型默认值的方法,已经针对数千个类型进行了测试:
/// <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");
}
在这些例子中,GetDefault方法实现在静态类DefaultValue中。使用以下语句调用此方法:
object Default = DefaultValue.GetDefault(someType);
object Default = someType.GetDefault();
这种第二种类型扩展方法是一个更简单的客户端代码语法,它消除了调用时引用包含DefaultValue类限定符的需要。
上述运行时形式的GetDefault与C#原始的“default”关键字具有相同的语义,并产生相同的结果。
要使用通用形式的GetDefault,可以访问以下函数:
/// <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));
}
一个调用通用形式的示例:
int? inDefaultVal = DefaultValue.GetDefault<int?>();
/// <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;
}
IsObjectSetToDefault
方法可以以其本地形式调用,也可以作为Type-class扩展进行访问。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);
}
}
default(System.Object) =
default(System.String) =
default(System.Int32) = 0
default(System.Nullable`1[System.Int32]) =
以下是一个函数,可以返回可空类型的默认值(换句话说,它会为Decimal
和Decimal?
都返回0):
public static object DefaultValue(Type maybeNullable)
{
Type underlying = Nullable.GetUnderlyingType(maybeNullable);
if (underlying != null)
return Activator.CreateInstance(underlying);
return Activator.CreateInstance(maybeNullable);
}
object a = myNullable;
编译器看到的是:object a = myNullable.HasValue ? myNullable.Value : null;
。因此,在技术上,Nullables的默认值确实具有其“基础”(通用)类型的默认值 - 但它没有暴露出来。 - AnorZaken