C# 默认值(T),集合为空,而不是null
我希望使用泛型方法返回传递类型的默认值,但对于集合类型,我希望得到空集合而不是null,例如:C# 默认值(T),集合为空,而不是null,c#,.net,C#,.net,我希望使用泛型方法返回传递类型的默认值,但对于集合类型,我希望得到空集合而不是null,例如: GetDefault<int[]>(); // returns empty array of int's GetDefault<int>(); // returns 0 GetDefault<object>(); // returns null GetDefault<IList<object>>(); // returns empty lis
GetDefault<int[]>(); // returns empty array of int's
GetDefault<int>(); // returns 0
GetDefault<object>(); // returns null
GetDefault<IList<object>>(); // returns empty list of objects
该实现在内部基于@280Z28答案:
public static class TypeExtensions
{
public static object GetDefault(this Type t)
{
var type = typeof(Default<>).MakeGenericType(t);
var property = type.GetProperty("Value", BindingFlags.Static | BindingFlags.Public);
var getaccessor = property.GetGetMethod();
return getaccessor.Invoke(null, null);
}
}
公共静态类类型扩展
{
公共静态对象GetDefault(此类型为t)
{
var type=typeof(默认值);
var property=type.GetProperty(“Value”,BindingFlags.Static | BindingFlags.Public);
var getaccessor=property.getMethod();
返回getaccessor.Invoke(null,null);
}
}
IList
不是集合类型,它是一个接口。您可以返回几十个可能的类
如果传入实际的集合类型,则可以执行以下操作:
public static T GetDefault<T>() where T : new
{
if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
{
return new T();
}
return default(T);
}
GetDefault<List<object>>();
您可以使用静态构造函数的魔力来有效地实现这一点。要在代码中使用默认值,只需使用
default.value
。在您的应用程序期间,仅对任何给定类型的T
计算一次该值
public static class Default<T>
{
private static readonly T _value;
static Default()
{
if (typeof(T).IsArray)
{
if (typeof(T).GetArrayRank() > 1)
_value = (T)(object)Array.CreateInstance(typeof(T).GetElementType(), new int[typeof(T).GetArrayRank()]);
else
_value = (T)(object)Array.CreateInstance(typeof(T).GetElementType(), 0);
return;
}
if (typeof(T) == typeof(string))
{
// string is IEnumerable<char>, but don't want to treat it like a collection
_value = default(T);
return;
}
if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
{
// check if an empty array is an instance of T
if (typeof(T).IsAssignableFrom(typeof(object[])))
{
_value = (T)(object)new object[0];
return;
}
if (typeof(T).IsGenericType && typeof(T).GetGenericArguments().Length == 1)
{
Type elementType = typeof(T).GetGenericArguments()[0];
if (typeof(T).IsAssignableFrom(elementType.MakeArrayType()))
{
_value = (T)(object)Array.CreateInstance(elementType, 0);
return;
}
}
throw new NotImplementedException("No default value is implemented for type " + typeof(T).FullName);
}
_value = default(T);
}
public static T Value
{
get
{
return _value;
}
}
}
公共静态类默认值
{
私有静态只读T_值;
静态默认值()
{
if(类型(T).IsArray)
{
if(typeof(T).GetArrayRank()>1)
_value=(T)(object)Array.CreateInstance(typeof(T).GetElementType(),新int[typeof(T).GetArrayRank());
其他的
_value=(T)(object)Array.CreateInstance(typeof(T).GetElementType(),0);
返回;
}
if(typeof(T)=typeof(string))
{
//字符串是IEnumerable,但不希望将其视为集合
_值=默认值(T);
返回;
}
if(typeof(IEnumerable).IsAssignableFrom(typeof(T)))
{
//检查空数组是否是T的实例
if(typeof(T).IsAssignableFrom(typeof(object[]))
{
_值=(T)(对象)新对象[0];
返回;
}
if(typeof(T).IsGenericType&&typeof(T).GetGenericArguments().Length==1)
{
Type elementType=typeof(T).GetGenericArguments()[0];
if(typeof(T).IsAssignableFrom(elementType.MakeArrayType()))
{
_value=(T)(object)Array.CreateInstance(elementType,0);
返回;
}
}
抛出新的NotImplementedException(“类型”+typeof(T).FullName没有实现默认值);
}
_值=默认值(T);
}
公共静态T值
{
得到
{
返回_值;
}
}
}
我认为您不能使用新的IList(),对吗?您需要一个实现。他可以直接返回new List(),它返回IList的一个具体实现。好吧,约束where T:new
阻止IList
从一开始就被传递,这很好,因为接口在确定实例化哪个类时是不明确的。+1用于typeof的强类型(IEnumerable).IsAssignableFrom(typeof(T)
。我想说点什么。@BenVoigt然而,这个解决方案不会阻止您在没有默认构造函数且不是IEnumerable的类上使用这个类。我认为这需要分成两个函数,如果编译器不够聪明,无法调用正确的版本(我不知道怎么做)如果IEnumerable未实现默认构造函数,则需要第一个函数调用第二个函数并抛出一个NotSupportedException
。@Scott:是的,如果这是一个问题,则可以摆脱约束并使用Activator
尝试从零参数构造函数获取实例。(如果Activator
失败,然后返回null
)我本来想抱怨线程安全性,但后来它在我的脑海中闪现,为什么你不必担心它。你能解释一下发生了什么,这样其他人就不会犯我即将犯的错误吗运行时保证静态构造函数会被调用一次(而且只有一次)在任何外部代码引用类的成员之前。静态构造函数始终是线程安全的,尽管存在其他问题,例如在静态构造函数引发异常时使类型不可用。不幸的是,您每次都不会返回空集合。您返回的集合实例与ugh empty最初是可变的,可能不会保持不变。@BenVoigt empty数组是不可变的。我希望Activator.CreateInstance
能适用于数组类型,但显然不行。我更新了代码,使其既适用于单维数组,也适用于多维数组。
public static T GetDefault<T>() where T : new
{
if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
{
return new T();
}
return default(T);
}
GetDefault<List<object>>();
public static T GetDefault<T>()
{
if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
{
if (typeof(T).IsGenericType)
{
Type T_template = typeof(T).GetGenericTypeDefinition();
if (T_template == typeof(IEnumerable<>))
{
return (T)Activator.CreateInstance(typeof(Enumerable).MakeGenericType(typeof(T).GetGenericArguments()));
}
if (T_template == typeof(IList<>))
{
return (T)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(T).GetGenericArguments()));
}
}
try {
return Activator.CreateInstance<T>();
}
catch (MissingMethodException) {} // no default exists for this type, fall-through to returning null
}
return default(T);
}
public static class Default<T>
{
private static readonly T _value;
static Default()
{
if (typeof(T).IsArray)
{
if (typeof(T).GetArrayRank() > 1)
_value = (T)(object)Array.CreateInstance(typeof(T).GetElementType(), new int[typeof(T).GetArrayRank()]);
else
_value = (T)(object)Array.CreateInstance(typeof(T).GetElementType(), 0);
return;
}
if (typeof(T) == typeof(string))
{
// string is IEnumerable<char>, but don't want to treat it like a collection
_value = default(T);
return;
}
if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
{
// check if an empty array is an instance of T
if (typeof(T).IsAssignableFrom(typeof(object[])))
{
_value = (T)(object)new object[0];
return;
}
if (typeof(T).IsGenericType && typeof(T).GetGenericArguments().Length == 1)
{
Type elementType = typeof(T).GetGenericArguments()[0];
if (typeof(T).IsAssignableFrom(elementType.MakeArrayType()))
{
_value = (T)(object)Array.CreateInstance(elementType, 0);
return;
}
}
throw new NotImplementedException("No default value is implemented for type " + typeof(T).FullName);
}
_value = default(T);
}
public static T Value
{
get
{
return _value;
}
}
}