我正在使用反射来遍历Type
的属性并将某些类型设置为其默认值。 现在,我可以对类型进行切换并显式设置default(Type)
,但我宁愿一行执行。 是否有程序等效的默认值?
#1楼
您可以使用PropertyInfo.SetValue(obj, null)
。 如果调用一个值类型,它将为您提供默认值。 .NET 4.0和.NET 4.5中记录了此行为。
#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;
}
}
}
#3楼
如果您使用的是.NET 4.0或更高版本,并且想要的程序版本不是在代码外部定义的规则的编纂,则可以创建一个Expression
,并即时对其进行编译和运行。
以下扩展方法将采用Type
并通过Expression
类的Default
方法获取从default(T)
返回的Default
:
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楼
这是优化的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;
}
}
}
#5楼
/// <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;
}
#6楼
暂时还找不到简单而优雅的东西,但是我有一个主意:如果您知道要设置的属性的类型,则可以编写自己的default(T)
。 有两种情况T
是值类型,而T
是引用类型。 您可以通过检查T.IsValueType
看到此T.IsValueType
。 如果T
是引用类型,则只需将其设置为null
。 如果T
是值类型,则它将具有默认的无参数构造函数,您可以调用该构造函数获取“空白”值。
#7楼
我做同样的任务。
//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;
}
#8楼
- 如果是值类型,请使用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
#9楼
选择的答案是一个很好的答案,但是要小心返回的对象。
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.");
#10楼
为什么您说泛型不适合?
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);
}
#11楼
对@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));
}
#12楼
为什么不通过反射调用返回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);
}
#13楼
表达式可以在这里提供帮助:
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。