通常检查不会在非约束类型中禁用空值的空值。 [英] Generically checking for null that won't box nullables on a non-constrained type.

查看:96
本文介绍了通常检查不会在非约束类型中禁用空值的空值。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有以下方法:

  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 .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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆