假设我有以下方法:
public static int CountNonNullMembers<T>(this IEnumerable<T> enumerable)
{
if (enumerable == null) throw new ArgumentNullException("enumerable");
int count = 0;
foreach (var x in enumerable)
{
if (x != null) count++;
}
return count;
}
我有这3个数组::
var ints = Enumerable.Range(0,10).ToArray();
var nullableInts = Array.ConvertAll(ints,x=>x as int?);
var strings = Array.ConvertAll(ints,x => x.ToString());
我写了一个小函数来做一个循环并将它计时一百万次迭代。将其应用于ints
和strings
,它在我的机器上完成约100毫秒。对于nullableInts
,需要2.5秒。
据我所知,int
上的null检查没有意义,因此编译器为不可为空的struct
类型提供了不同的模板,这将删除空检查。但Nullable<T>
没有将空检查转换为x.HasValue
的模板。如果我有一个无约束的函数,我怎么能做一个表现良好的空检查?我不能使用EqualityComparer<T>
,因为null可能不是T
的成员,因为没有约束。
也不可能有因约束而不同的重载,所以我不能说,structs
有一个,Nullable<T>
有一个,类有一个。
该方法的调用者是非约束的。这只是一个例子(不是实际的方法);调用方法是非约束的。我需要针对非null成员做一些工作,这是一种通用方法。我想我可以编写一个不执行检查的版本与执行该版本的版本(因此具有不同的签名),但它看起来非常难看且不需要。
此外,扩展方法.Count
莫名其妙地为NullableInts
和strings
执行,(同样糟糕),所以它确实不是正确的方法。这可能是委托调用,但我对此表示怀疑。使用UnboxT
的{{1}}样式方法可以更好地执行。
好吧,非常奇怪的是将计数体转换为非常好的表现:
Check<T>.IfNull
为什么?
答案 0 :(得分:1)
您可以constrain generic type parameters引用类型或值类型:
public static int CountNonNull<T>(this IEnumerable<T> source)
where T : class
{
return source.Count(x => x != null);
}
public static int CountNonNull<T>(this IEnumerable<Nullable<T>> source)
where T : struct
{
return source.Count(x => x.HasValue);
}
对于不可为空的结构,您不需要重载,因为无论如何它们都不能为空。
答案 1 :(得分:1)
使用UnboxT
方法有效。但我也想要一些不需要创建静态类型的东西::
public static class Check<T>
{
public static readonly Predicate<T> IfNull = CreateIfNullDelegate();
private static bool AlwaysFalse(T obj)
{
return false;
}
private static bool ForRefType(T obj)
{
return object.ReferenceEquals(obj, null);
}
private static bool ForNullable<Tu>(Tu? obj) where Tu:struct
{
return !obj.HasValue;
}
private static Predicate<T> CreateIfNullDelegate()
{
if (!typeof(T).IsValueType)
return ForRefType;
else
{
Type underlying;
if ((underlying = Nullable.GetUnderlyingType(typeof(T))) != null)
{
return Delegate.CreateDelegate(
typeof(Predicate<T>),
typeof(Check<T>)
.GetMethod("ForNullable",BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(underlying)
) as Predicate<T>;
}
else
{
return AlwaysFalse;
}
}
}
}
使用这种方法,一切都表现相同。 Strings
表现更差,但并不比其他任何事情都差。
答案 2 :(得分:0)
虽然不一定比你的方法更好,但它不需要整个班级:
static Dictionary<Type, object> NullChecks = new Dictionary<Type, object>();
public static Func<T, bool> MakeNullCheck<T>()
{
object obj;
Func<T, bool> func;
if (NullChecks.TryGetValue(typeof(T), out obj))
return (Func<T, bool>)obj;
if (typeof(T).IsClass)
func = x => x != null;
else if (Nullable.GetUnderlyingType(typeof(T)) != null)
{
var param = Expression.Parameter(typeof(T));
func = Expression.Lambda<Func<T, bool>>(
Expression.Property(param, typeof(T).GetProperty("HasValue")), param).Compile();
}
else
func = x => false;
NullChecks[typeof(T)] = func;
return func;
}