如何使用反射获取值类型的默认值

10
如果我有一个泛型类型参数是值类型,并且我想知道一个值是否等于默认值,我会像这样测试它:
static bool IsDefault<T>(T value){
    where T: struct
    return value.Equals(default(T));
}

如果我没有一个泛型类型参数,那么似乎我必须使用反射。如果这个方法必须适用于所有的值类型, 那么有没有比我这里做的更好的测试方式呢?
static bool IsDefault(object value){
   if(!(value is ValueType)){
      throw new ArgumentException("Precondition failed: Must be a ValueType", "value");
   }
   var @default = Activator.CreateInstance(value.GetType());
   return value.Equals(@default);  
}

顺带一提,我在评估可空结构时是否有遗漏的地方?


1
看起来还不错。好在 value is ValueType 的测试也会检查 null。 :) - cdhowie
两个问题:1. 为什么不使用通用方法?2.既然你还是要抱怨,为什么不将第二个方法的参数设置为“ValueType”呢? - dlev
2
或者执行 if (!(value is ValueType)) return value == null; - cdhowie
4个回答

8

我发现以下扩展方法非常有用,适用于所有类型:

public static object GetDefault(this Type t)
{
    return t.IsValueType ? Activator.CreateInstance(t) : null;
}

public static T GetDefault<T>()
{
    var t = typeof(T);
    return (T) GetDefault(t);
}

public static bool IsDefault<T>(T other)
{
    T defaultValue = GetDefault<T>();
    if (other == null) return defaultValue == null;
    return other.Equals(defaultValue);
}

1
这似乎在纯反射场景中不起作用,其中您不知道任何一个类型,传入的是一个对象。 因此,“IsDefault”中的“T”只是对象,“.Equals”仅检查引用相等性。 至少在我的情况下,打包的“0”与“Activator.CreateInstance(property.PropertyType)”不相等,其中“propertyType”为“int”。 - Maslow
我一直在尝试您的解决方案并发现了一个更短的(我相信这也应该正常工作):public static object GetDefault<T>(T obj) { return default(T); } - Marko Gresak

7

虽然这是一个老问题,但是对于我来说,接受的答案并不适用,所以我提交了这个答案(可能可以改进):

public static object GetDefault(this Type type)
{   
    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
    {
        var valueProperty = type.GetProperty("Value");
        type = valueProperty.PropertyType;
    }

    return type.IsValueType ? Activator.CreateInstance(type) : null;
}

以下是相关结果:
typeof(int).GetDefault();       // returns 0
typeof(int?).GetDefault();      // returns 0
typeof(DateTime).GetDefault();  // returns 01/01/0001 00:00:00
typeof(DateTime?).GetDefault(); // returns 01/01/0001 00:00:00
typeof(string).GetDefault();    // returns null
typeof(Exception).GetDefault(); // returns null

FYI,这是答案 - tacos_tacos_tacos

2

为了简化,我需要将ValueType作为参数:

static bool IsDefault(ValueType value){
   var @default = Activator.CreateInstance(value.GetType());
   return value.Equals(@default);  
}

cdhowie的评论让我重新考虑这个解决方案,因为它没有处理潜在的“NullReferenceException”。一定要处理这种情况,如果参数为空,则抛出“ArgumentNullException”。 - Gene C

0
另外需要注意的是,您在评估可空结构时忽略了一些内容。通过将object作为参数,您要求调用代码对Nullable<T>类型进行装箱(将其转换为null或其T值)。因此,如果传递一个可空类型,您的is/throw将会抛出异常,因为null永远不会是值类型。
编辑:正如@cdhowie所说,您需要检查是否为空。这也适用于可空类型。

在考虑可空结构体时需要注意的另一件事是,无法区分例如值为零的 int 和值为零的 int?,即使零是前者类型的默认值,但不是后者的默认值。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接