默认(类型)的编程等效项

Posted

技术标签:

【中文标题】默认(类型)的编程等效项【英文标题】:Programmatic equivalent of default(Type) 【发布时间】:2010-09-24 10:28:00 【问题描述】:

我正在使用反射循环遍历Type 的属性并将某些类型设置为默认值。现在,我可以切换类型并显式设置default(Type),但我宁愿在一行中完成。是否有默认的编程等效项?

【问题讨论】:

这应该可以工作: Nullable a = new Nullable().GetValueOrDefault(); 【参考方案1】: 如果是值类型,请使用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

【讨论】:

这将返回一个装箱的值类型,因此不完全等同于 default(Type)。但是,它与没有泛型的情况一样接近。 那又怎样?如果你找到 default(T) != (T)(object)default(T) && !(default(T) != default(T)) 的类型,那么你有一个参数,否则它是否被装箱并不重要,因为它们是等价的。 谓词的最后一部分是为了避免因运算符重载而作弊......可以让default(T) != default(T) 返回false,这就是作弊! =) 这对我帮助很大,但我认为我应该添加一个可能对某些搜索这个问题的人有用的东西 - 如果你想要一个 array,还有一个等效的方法给定类型的,你可以使用Array.CreateInstance(type, length)获取它。 你不担心创建一个未知值类型的实例吗?这可能会产生附带影响。【参考方案2】:

为什么不用反射调用返回 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);
    

【讨论】:

这太棒了,因为它非常简单。虽然这不是最好的解决方案,但它是一个需要牢记的重要解决方案,因为这种技术在很多类似的情况下都很有用。 如果您改为调用通用方法“GetDefault”(重载),请执行以下操作:this.GetType().GetMethod("GetDefault", new Type[0])。 请记住,这个实现比接受的答案慢得多(由于反射)。它仍然可行,但您需要为 GetMethod()/MakeGenericMethod() 调用设置一些缓存以提高性能。 类型参数可能是无效的。例如。 void 方法的 MethodBase.ResultType() 将返回名称为“Void”或 FullName 为“System.Void”的 Type 对象。因此我设置了一个警卫: if (t.FullName=="System.Void") return null;感谢您的解决方案。 如果可以的话,最好使用nameof(GetDefaultGeneric),而不是"GetDefaultGeneric"【参考方案3】:

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

【讨论】:

对于这个特定的问题 - 通过类型的属性循环并将它们设置为“默认” - 这非常有效。我在使用反射从 SqlDataReader 转换为对象时使用它。【参考方案4】:

如果您使用的是 .NET 4.0 或更高版本,并且您想要一个不是在代码之外定义的规则编纂的程序化版本,您可以创建一个Expression,编译并运行它是即时的。

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

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实例调用这个,并且不要经常使用它,缓存消耗的内存可能会超过好处。

【讨论】:

'return type.IsValueType 的性能? Activator.CreateInstance(type) : null;'比 e.Compile()() 快 1000 倍; @Cyrus 我很确定如果你缓存e.Compile(),情况会相反。这就是表达的全部意义。 运行基准测试。显然,e.Compile() 的结果应该被缓存,但假设,这种方法的速度大约是 14 倍,例如long。有关基准和结果,请参阅 gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b。 出于兴趣,为什么缓存e.Compile() 而不是e.Compile()()?即类型的默认类型可以在运行时更改吗?如果不是(我相信是这种情况),您可以只存储缓存结果而不是编译后的表达式,这应该会进一步提高性能。 @JohnLBevan - 是的,然后你使用什么技术来获得结果并不重要 - 所有这些都将具有极快的摊销性能(字典查找)。【参考方案5】:

为什么你说泛型不在图片范围内?

    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。 在运行时创建泛型方法,然后连续使用数千次,成本是多少? 我在想这样的事情。对我来说最好和最优雅的解决方案。甚至适用于 Compact Framework 2.0。如果你担心性能,你总是可以缓存泛型方法,不是吗? 这个解决方案非常适合!谢谢!【参考方案6】:

这是 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;
        
    

【讨论】:

返回的简写版本:return type.IsValueType ? typeDefaults.GetOrAdd(type, Activator.CreateInstance) : null; 可变结构呢?你知道修改盒装结构的字段是可能的(并且是合法的),从而改变数据吗? @IllidanS4 作为方法的名称意味着这仅适用于默认 ValueType 的值。【参考方案7】:

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

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.");

【讨论】:

true,但这也适用于默认(字符串),以及其他所有引用类型...... string 是一种奇怪的鸟——它也是一种可以返回 null 的值类型。如果您希望代码返回 string.empty,只需为其添加一个特殊情况 @Dror - 字符串是不可变的引用类型,而不是值类型。 @kronoz 你说得对 - 我的意思是可以根据需要通过返回 string.empty 或 null 来处理字符串。【参考方案8】:

我做同样的任务。

//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;
    

【讨论】:

【参考方案9】:

相当于 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;
        
    

【讨论】:

【参考方案10】:

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

    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();
    

我没有测试这个 sn-p,但我认为它应该为引用类型产生“类型化”的空值..

【讨论】:

"typed" nulls - 解释一下。你要返回什么对象?如果您返回类型为type 的对象,但其值为null,则除了null 之外,它没有-不能-具有任何其他信息。您无法查询 null 值,并找出它应该是什么类型。如果你不返回 null,而是返回 .. 我不知道是什么 ..,那么它的行为就不会像 null【参考方案11】:

对@Rob Fonseca-Ensor's solution 稍作调整:以下扩展方法也适用于 .Net Standard,因为我使用 GetRuntimeMethod 而不是 GetMethod。

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));

【讨论】:

【参考方案12】:
 /// <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;
    

【讨论】:

不适用于Nullable&lt;T&gt; 类型:它不会返回与default(Nullable&lt;T&gt;) 等效的null。 Dror 接受的答案效果更好。【参考方案13】:

这应该有效: Nullable&lt;T&gt; a = new Nullable&lt;T&gt;().GetValueOrDefault();

【讨论】:

它只适用于 struct,因为 Nullable 对 T: where T: struct 有通用约束

以上是关于默认(类型)的编程等效项的主要内容,如果未能解决你的问题,请参考以下文章

C# 的默认值(T)的 VB 等效项

c# 2.0 中可空值的默认值

Spring boot Embedded tomcat 中 jersey Servlet 注册的编程等效项

Java 中协议缓冲区分隔的 I/O 函数是不是有 C++ 等效项?

SQL Server 数据类型的 C# 等效项

MySQL 的 TEXT 类型的 Oracle 等效项