通常检查不会在非约束类型中禁用空值的空值。 [英] Generically checking for null that won't box nullables on a non-constrained type.
问题描述
public static int CountNonNullMembers< T>(this IEnumerable< T> enumerable)
{
if(enumerable == null)throw new ArgumentNullException(enumerable);
int count = 0;
foreach(可枚举的var x)
{
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
和字符串
,它在我的机器上完成大约100 ms。对于 nullableInts
,它需要2.5秒。
据我所知,在 int
上检查null是没有意义的,所以编译器为不可为空的 struct
类型,删除空检查。但 Nullable< T>
没有将空检查转换为 x.HasValue
的模板。如果我有一个不受约束的函数,我该如何做一个空值检查,表现良好?我不能使用 EqualityComparer< T>
,因为null可能不是 T
的成员,因为没有约束。
另外,不可能有因约束而不同的重载,所以我不能说有一个用于结构
,其中一个用于 Nullable< T>
,另一个用于类。
限制。这只是一个例子(不是实际的方法)。该方法调用是不受限制的。我需要对非空成员做一些工作,这是一种通用的方法。我想我可以写一个不支持这种检查的版本(因此具有不同的签名),但它看起来非常丑陋,并且不需要。
另外,对于 NullableInts $ c,扩展方法
.Count
莫名其妙地执行$ c>和 strings
,(同样不好),所以它确实不是正确的方法。这可能是委托调用,但我怀疑它。使用 Check< T> .IfNull< / code>的
UnboxT
样式方法执行得更好。
好的,真正奇怪的将计数主体切换到这个表现非常好:
public static int CountNonNullMembers< T> (这个IEnumerable< T>可枚举)
{
return enumerable.Count(Check< T> .IfNull.Invoke);
}
为什么?
使用 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)其中Tu:struct
{
return!obj.HasValue;
}
private static Predicate< T> CreateIfNullDelegate()
{
if(!typeof(T).IsValueType)
返回ForRefType;
else
{
类型底层;
if((underlying = Nullable.GetUnderlyingType(typeof(T)))!= null)
{
return Delegate.CreateDelegate(
typeof(Predicate< T>),$作为Predicate< T>的b $ b typeof(Check< T>)
.GetMethod(ForNullable,BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(基础)
)
}
else
{
return AlwaysFalse;
}
}
}
}
使用这种方法的一切表现都差不多。 字符串
表现更差,但并不比其他所有事情都糟糕。
Let's say I have the following method:
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;
}
And I have these 3 arrays::
var ints = Enumerable.Range(0,10).ToArray();
var nullableInts = Array.ConvertAll(ints,x=>x as int?);
var strings = Array.ConvertAll(ints,x => x.ToString());
I wrote a little function to do a loop and time it for a million iterations. Applying it to ints
and strings
, it finishes in about 100 ms on my machine. For nullableInts
, it takes 2.5 seconds.
As I understand the check for null on int
doesn't make sense, so the compiler has a different template for non-nullable struct
types, that removes null checks. But Nullable<T>
does not have a template that converts the null check to x.HasValue
. If I have an unconstrained function, how can I do a null check that will perform well? I can't use EqualityComparer<T>
, as null might not be a member of T
as there is no constraint.
Also it's impossible to have overloads that differ by constraint, so I can't, say, have one for structs
, one for Nullable<T>
, and one for classes.
The caller of the method is non-constrained. This is just an example (not the actual method); the method calling is non-constrained. I need to do some work against non-null members, and it's a generic method. I suppose I could write a version that doesn't do the check vs one that does (and consequently has a different signature), but it's seems very ugly and unneeded.
Also, the extension method .Count
inexplicably performs horribly for NullableInts
and strings
, (equally bad), so it really isn't the right approach. This might be the delegate invocation, but I doubt it. Using the UnboxT
style method of Check<T>.IfNull
performs a lot better.
Okay, really weird switching the body of the count to this performs great:
public static int CountNonNullMembers<T>(this IEnumerable<T> enumerable)
{
return enumerable.Count(Check<T>.IfNull.Invoke);
}
Why?
Using the UnboxT
approach works. But I'd also like something that doesn't require creating a static type::
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;
}
}
}
}
Using this approach everything performs about the same. Strings
performs worse, but not so much worse than everything else.
这篇关于通常检查不会在非约束类型中禁用空值的空值。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!