默认的编程等效(类型)

问题描述 投票:0回答:15

我正在使用反射来遍历

Type
的属性并将某些类型设置为默认值。现在,我可以切换类型并明确设置
default(Type)
,但我宁愿在一行中完成。是否有默认的编程等价物?

c# reflection default
15个回答
794
投票
  • 在值类型的情况下使用 Activator.CreateInstance 它应该可以正常工作。
  • 使用引用类型时只需返回 null
public static object GetDefault(Type type)
{
   if(type.IsValueType)
   {
      return Activator.CreateInstance(type);
   }
   return null;
}

在较新版本的.net如.net standard中,

type.IsValueType
需要写成
type.GetTypeInfo().IsValueType


116
投票

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

97
投票

您可以使用

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


58
投票

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


41
投票

你为什么说仿制药不在画面中?

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

28
投票

这是优化的弗莱姆解:

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

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

5
投票

我像这样做同样的任务。

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

3
投票

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

3
投票

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

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

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


3
投票

@Rob Fonseca-Ensor 的解决方案进行微调:以下扩展方法也适用于 .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));
}

0
投票

这里有几个答案使用

Activator.CreateInstance
。但是,由于做出了这些答案,C# 和 .NET 进行了更改,允许用户使用无参数构造函数定义结构(文档链接)。
Activator.CreateInstance
如果定义了就会执行这样一个无参数的构造函数,你将不再获得默认实例。

相反,您可以使用

FormatterServices.GetUninitializedObject
,像这样:

//using System.Runtime.Serialization
public static object GetDefault(Type type)
{
    // it's very important to check IsValueType before calling GetUninitializedObject
    // GetUninitializedObject is valid for reference types, but it will not return null
    if (type.IsValueType)
    {
        return FormatterServices.GetUninitializedObject(type);
    }
    return null;
}

另一种选择,您可以将其用作单线,使用

Array.CreateInstance

// works for both reference types and value types
static object GetDefault(Type type) => Array.CreateInstance(type, 1).GetValue(0);

-1
投票
 /// <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;
    }

-1
投票

这应该有效:

Nullable<T> a = new Nullable<T>().GetValueOrDefault();


-1
投票

如果你已经创建了一个对象,你可以尝试...

var yourObj = new yourObj();
var properties = yourObj.GetType().GetProperties();

foreach (var p in properties)
{
    // you can get default value for each property
    var defaultValue = p.GetValue(yourObj, null);
}
© www.soinside.com 2019 - 2024. All rights reserved.