程序化等效的默认值(类型)


514

我正在使用反射来遍历Type的属性并将某些类型设置为其默认值。现在,我可以在类型上进行切换并default(Type)显式设置,但我宁愿一行执行。是否有程序等效的默认值?


这应该起作用:Nullable <T> a = new Nullable <T>()。GetValueOrDefault();
舞者42

Answers:


694
  • 如果是值类型,请使用Activator.CreateInstance,它应该可以正常工作。
  • 使用引用类型时,只需返回null
public static object GetDefault(Type type)
{
   if(type.IsValueType)
   {
      return Activator.CreateInstance(type);
   }
   return null;
}

在.net标准等新版.net中,type.IsValueType需要写成type.GetTypeInfo().IsValueType


22
这将返回一个装箱的值类型,因此与default(Type)并不完全相同。但是,它与没有泛型的情况一样接近。
罗素·吉丁斯

8
所以呢?如果找到带有default(T) != (T)(object)default(T) && !(default(T) != default(T))参数的类型,否则将其装箱与否无关紧要,因为它们是等效的。
米格尔·安吉洛

7
谓词的最后一部分是避免因运算符重载而作弊...可能使default(T) != default(T)return为false,这就是作弊!=)
Miguel Angelo

4
这对我很有帮助,但是我想我应该添加一件事,这可能对某些搜索此问题的人有用-如果您想要给定类型的数组,也可以使用等效方法,并且可以使用获得它Array.CreateInstance(type, length)
Darrel Hoffman

4
您是否担心创建未知值类型的实例?这可能会产生附带影响。
ygormutti

103

为什么不通过反射调用返回default(T)的方法呢?您可以将任何类型的GetDefault用于:

    public object GetDefault(Type t)
    {
        return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null);
    }

    public T GetDefaultGeneric<T>()
    {
        return default(T);
    }

7
这很出色,因为它是如此简单。虽然这不是最佳解决方案,但它是一个重要的解决方案,因为此技术在许多类似情况下都非常有用。
Configurator

如果您改为调用通用方法“ GetDefault”(重载),请执行以下操作:this.GetType()。GetMethod(“ GetDefault”,new Type [0])。<AS_IS>
Stefan Steiger

2
请记住,此实现比接受的答案要慢得多(由于反射)。它仍然可行,但是您需要为GetMethod()/ MakeGenericMethod()调用设置一些缓存以提高性能。
道格

1
类型参数可能为空。例如,void方法的MethodBase.ResultType()将返回名称为“ Void”或FullName为“ System.Void”的Type对象。因此,我加了个警惕:if(t.FullName ==“ System.Void”)返回null;感谢您的解决方案。
Valo

8
nameof(GetDefaultGeneric)如果可以的话,请更好地使用它,而不是"GetDefaultGeneric"
Mugen

87

您可以使用PropertyInfo.SetValue(obj, null)。如果调用一个值类型,它将为您提供默认值。.NET 4.0.NET 4.5中记录此行为。


7
对于这个特定的问题-循环通过类型的属性并将其属性设置为“默认”-这非常有用。当使用反射从SqlDataReader转换为对象时,会使用它。
亚诺·彼得斯

57

如果您使用的是.NET 4.0或更高版本,并且希望使用的程序版本不是在代码外部定义的规则的编纂,则可以实时创建Expression,编译和运行它。

以下扩展方法将采用Type并获取default(T)通过类上的Default方法返回的值Expression

public static T GetDefaultValue<T>()
{
    // We want an Func<T> which returns the default.
    // Create that expression here.
    Expression<Func<T>> e = Expression.Lambda<Func<T>>(
        // The default value, always get what the *code* tells us.
        Expression.Default(typeof(T))
    );

    // Compile and return the value.
    return e.Compile()();
}

public static object GetDefaultValue(this Type type)
{
    // Validate parameters.
    if (type == null) throw new ArgumentNullException("type");

    // We want an Func<object> which returns the default.
    // Create that expression here.
    Expression<Func<object>> e = Expression.Lambda<Func<object>>(
        // Have to convert to object.
        Expression.Convert(
            // The default value, always get what the *code* tells us.
            Expression.Default(type), typeof(object)
        )
    );

    // Compile and return the value.
    return e.Compile()();
}

您还应该基于来缓存上面的值Type,但是要知道,如果您是针对大量Type实例调用此值,并且不经常使用它,则缓存消耗的内存可能会超过收益。


4
返回值类型的性能。Activator.CreateInstance(type):null;' 比e.Compile()()快1000倍;
赛勒斯(Cyrus)2014年

1
@Cyrus我非常确定,如果您缓存,它将是另一回事e.Compile()。这就是表达的重点。
nawfal

2
跑基准。显然,e.Compile()应该缓存的结果,但是假设该方法的速度大约是例如的14倍long。有关基准和结果,请参见gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b
Pieter van Ginkel,

3
出于兴趣,为什么要缓存e.Compile()而不是e.Compile()()?即,类型的默认类型可以在运行时更改吗?如果不是这样(我相信是这样),则可以仅存储结果缓存,而不是存储编译后的表达式,这将进一步提高性能。
JohnLBevan '18年

3
@JohnLBevan-是的,然后使用哪种技术来获取结果并不重要-所有这些都将具有极快的摊销性能(字典查找)。
Daniel Earwicker

38

为什么您说泛型不适合?

    public static object GetDefault(Type t)
    {
        Func<object> f = GetDefault<object>;
        return f.Method.GetGenericMethodDefinition().MakeGenericMethod(t).Invoke(null, null);
    }

    private static T GetDefault<T>()
    {
        return default(T);
    }

无法解析符号方法。在Windows上使用PCL。
心教堂

1
在运行时创建通用方法,然后连续使用数千次,它的成本是多少?
C. Tewalt

1
我在想这样的事情。对我来说最好,最优雅的解决方案。即使在Compact Framework 2.0上也可以使用。如果您担心性能,可以始终缓存通用方法,不是吗?
巴特2015年

此解决方案完全适合!谢谢!
Lachezar Lalov

25

这是优化的Flem解决方案:

using System.Collections.Concurrent;

namespace System
{
    public static class TypeExtension
    {
        //a thread-safe way to hold default instances created at run-time
        private static ConcurrentDictionary<Type, object> typeDefaults =
           new ConcurrentDictionary<Type, object>();

        public static object GetDefaultValue(this Type type)
        {
            return type.IsValueType
               ? typeDefaults.GetOrAdd(type, Activator.CreateInstance)
               : null;
        }
    }
}

2
返回的return type.IsValueType ? typeDefaults.GetOrAdd(type, Activator.CreateInstance) : null;
简写形式

3
可变结构呢?您是否知道有可能(合法)修改盒装结构的字段,以便更改数据?
IllidanS4希望莫妮卡回到2015年

@ IllidanS4作为方法的名称意味着这仅适用于默认ValueType的值。
aderesh

8

选择的答案是一个很好的答案,但是要小心返回的对象。

string test = null;
string test2 = "";
if (test is string)
     Console.WriteLine("This will never be hit.");
if (test2 is string)
     Console.WriteLine("Always hit.");

外推...

string test = GetDefault(typeof(string));
if (test is string)
     Console.WriteLine("This will never be hit.");

14
是的,但是对于default(string)以及其他所有引用类型也是如此...
TDaver 2011年

字符串是一个奇怪的鸟-是一个值类型,也可以返回null。如果您想让代码返回string.empty,只需为其添加一个特殊情况
Dror Helper

15
@Dror-字符串是不可变的引用类型,而不是值类型。
ljs 2011年

@kronoz你是对的-我的意思是可以根据需要返回string.empty或null来处理字符串。
Dror Helper

5

表达式可以在这里提供帮助:

    private static Dictionary<Type, Delegate> lambdasMap = new Dictionary<Type, Delegate>();

    private object GetTypedNull(Type type)
    {
        Delegate func;
        if (!lambdasMap.TryGetValue(type, out func))
        {
            var body = Expression.Default(type);
            var lambda = Expression.Lambda(body);
            func = lambda.Compile();
            lambdasMap[type] = func;
        }
        return func.DynamicInvoke();
    }

我没有测试此代码段,但我认为它应该为引用类型生成“类型化”的null。


1
"typed" nulls-解释一下。您要返回什么对象?如果返回类型为object的对象type,但其值为null,则该对象不会-不能-拥有除其以外的任何其他信息null。您无法查询null值,也不知道它应该是什么类型。如果您不返回null,而是返回..我不知道什么..,那么它将不会像null
制造商史蒂夫

3

尚无法找到任何简单而优雅的东西,但是我有一个主意:如果您知道要设置的属性的类型,则可以编写自己的default(T)。有两种情况- T是值类型和T引用类型。您可以通过选中查看T.IsValueType。如果T是引用类型,则只需将其设置为null。如果T是值类型,则它将具有默认的无参数构造函数,您可以调用该构造函数获取“空白”值。


3

我做同样的任务。

//in MessageHeader 
   private void SetValuesDefault()
   {
        MessageHeader header = this;             
        Framework.ObjectPropertyHelper.SetPropertiesToDefault<MessageHeader>(this);
   }

//in ObjectPropertyHelper
   public static void SetPropertiesToDefault<T>(T obj) 
   {
            Type objectType = typeof(T);

            System.Reflection.PropertyInfo [] props = objectType.GetProperties();

            foreach (System.Reflection.PropertyInfo property in props)
            {
                if (property.CanWrite)
                {
                    string propertyName = property.Name;
                    Type propertyType = property.PropertyType;

                    object value = TypeHelper.DefaultForType(propertyType);
                    property.SetValue(obj, value, null);
                }
            }
    }

//in TypeHelper
    public static object DefaultForType(Type targetType)
    {
        return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
    }

2

等同于Dror的答案,但作为扩展方法:

namespace System
{
    public static class TypeExtensions
    {
        public static object Default(this Type type)
        {
            object output = null;

            if (type.IsValueType)
            {
                output = Activator.CreateInstance(type);
            }

            return output;
        }
    }
}

2

@Rob Fonseca-Ensor解决方案的调整:由于我使用GetRuntimeMethod而不是GetMethod,因此以下扩展方法也适用于.Net Standard。

public static class TypeExtensions
{
    public static object GetDefault(this Type t)
    {
        var defaultValue = typeof(TypeExtensions)
            .GetRuntimeMethod(nameof(GetDefaultGeneric), new Type[] { })
            .MakeGenericMethod(t).Invoke(null, null);
        return defaultValue;
    }

    public static T GetDefaultGeneric<T>()
    {
        return default(T);
    }
}

...以及针对那些关心质量的人员的相应单元测试:

[Fact]
public void GetDefaultTest()
{
    // Arrange
    var type = typeof(DateTime);

    // Act
    var defaultValue = type.GetDefault();

    // Assert
    defaultValue.Should().Be(default(DateTime));
}

0
 /// <summary>
    /// returns the default value of a specified type
    /// </summary>
    /// <param name="type"></param>
    public static object GetDefault(this Type type)
    {
        return type.IsValueType ? (!type.IsGenericType ? Activator.CreateInstance(type) : type.GenericTypeArguments[0].GetDefault() ) : null;
    }

2
Nullable<T>类型不起作用:它不返回default(Nullable<T>)应该等于的值null。Dror接受的答案效果更好。
心教堂

可以使用反射来检查是否可以为空...
Dancer42

0

这应该工作: Nullable<T> a = new Nullable<T>().GetValueOrDefault();

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.