【发布时间】:2010-09-24 10:28:00
【问题描述】:
我正在使用反射循环遍历Type 的属性并将某些类型设置为默认值。现在,我可以切换类型并显式设置default(Type),但我宁愿在一行中完成。是否有默认的编程等效项?
【问题讨论】:
-
这应该可以工作: Nullable
a = new Nullable ().GetValueOrDefault();
标签: c# reflection default
我正在使用反射循环遍历Type 的属性并将某些类型设置为默认值。现在,我可以切换类型并显式设置default(Type),但我宁愿在一行中完成。是否有默认的编程等效项?
【问题讨论】:
标签: c# reflection default
public static object GetDefault(Type type)
{
if(type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
在.net标准等较新版本的.net中,type.IsValueType需要写成type.GetTypeInfo().IsValueType
【讨论】:
default(T) != (T)(object)default(T) && !(default(T) != default(T)) 的类型,那么你有一个参数,否则它是否被装箱并不重要,因为它们是等价的。
default(T) != default(T) 返回false,这就是作弊! =)
Array.CreateInstance(type, length)获取它。
为什么不用反射调用返回 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);
}
【讨论】:
nameof(GetDefaultGeneric),而不是"GetDefaultGeneric"
您可以使用PropertyInfo.SetValue(obj, null)。如果在值类型上调用,它将为您提供默认值。此行为记录在 in .NET 4.0 和 in .NET 4.5。
【讨论】:
如果您使用的是 .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实例调用这个,并且不要经常使用它,缓存消耗的内存可能会超过好处。
【讨论】:
e.Compile(),情况会相反。这就是表达的全部意义。
e.Compile() 的结果应该被缓存,但假设,这种方法的速度大约是 14 倍,例如long。有关基准和结果,请参阅 gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b。
e.Compile() 而不是e.Compile()()?即类型的默认类型可以在运行时更改吗?如果不是(我相信是这种情况),您可以只存储缓存结果而不是编译后的表达式,这应该会进一步提高性能。
为什么你说泛型不在图片范围内?
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);
}
【讨论】:
这是 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;
选择的答案是一个很好的答案,但要小心返回的对象。
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.");
【讨论】:
我做同样的任务。
//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;
}
【讨论】:
相当于 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;
}
}
}
【讨论】:
表达式可以在这里提供帮助:
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。
对@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));
}
【讨论】:
/// <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<T> 类型:它不会返回与default(Nullable<T>) 等效的null。 Dror 接受的答案效果更好。
这应该有效:
Nullable<T> a = new Nullable<T>().GetValueOrDefault();
【讨论】: