简体   繁体   English

对引用和值类型进行空检查

[英]Null checking on reference and value types

(Update - from comments) Question: Is there any advantage of using one extension method over the other? (更新-来自注释)问题:使用一种扩展方法相对于另一种扩展方法有什么优势?

From a discussion that I am having in my codeproject article on extension methods, I am unsure whether the following is correct or not. 从我在代码项目文章中有关扩展方法的讨论中,我不确定以下内容是否正确。

Currently, I have the following extension method: 目前,我有以下扩展方法:

public static bool In<T>(this T source, params T[] list)
{
    if (null == source) throw new ArgumentNullException("source");
    return list.Contains(source);
}

Which works as expected. 哪个按预期工作。 It has been suggested in the comments that I change it so that it only checks reference types like so: 我在注释中建议对它进行更改,以便仅检查引用类型,如下所示:

public static bool In<T>(this T source, params T[] list)
{
     if (!typeof(T).IsValueType)
     {
         if (Equals(source, default(T))) throw new ArgumentNullException("source");
     }
     return list.Contains(source);
}

Again that works as expected. 再次按预期工作。 Is there any advantage of the second method over the first given that running a quick benchmark, we are talking about 0.001 of a second difference for 10000 runs. 考虑到运行快速基准测试, 第二种方法相对于第一种方法是否有任何优势 ,我们正在谈论10000次运行的第二种差异为0.001。

Output of benchmark (Core i3 @ 4ghz, RAID 0 ssd's): 基准输出(Core i3 @ 4ghz,RAID 0 ssd):

Testing performance...

Value type, original: 00:00:00.0033289
Value type, from code project: 00:00:00.0033027
Reference type, original: 00:00:00.0076951
Reference type, from code project: 00:00:00.0068459

Benchmark code: 基准代码:

        Console.WriteLine("Testing performance...");
        Console.WriteLine("");

        const Int32 _runs = 10000;

        Stopwatch sw = new Stopwatch();
        Console.Write("Value type, original: ");
        sw.Start();
        for (Int32 i = 0; i < _runs; i++)
        {
            try
            {
                i.In(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            }
            catch (Exception)
            {
                // do nothing    
            }
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed.ToString());

        sw = new Stopwatch();
        Console.Write("Value type, from code project: ");
        sw.Start();
        for (Int32 i = 0; i < _runs; i++)
        {
            try
            {
                i.In2(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            }
            catch (Exception)
            {
                // do nothing    
            }
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed.ToString());


        sw = new Stopwatch();
        Console.Write("Reference type, original: ");
        sw.Start();
        for (Int32 i = 0; i < _runs; i++)
        {
            try
            {
                "This String".In("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
            }
            catch (Exception)
            {
                // do nothing    
            }
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed.ToString());

        sw = new Stopwatch();
        Console.Write("Reference type, from code project: ");
        sw.Start();
        for (Int32 i = 0; i < _runs; i++)
        {
            try
            {
                "This String".In("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
            }
            catch (Exception)
            {
                // do nothing    
            }
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed.ToString());

        Console.WriteLine("");
        Console.ReadLine();


public static bool In<T>(this T source, params T[] list)
{
    if (source == null) throw new ArgumentNullException("source");
    return list.Contains(source);
}

public static bool In2<T>(this T source, params T[] list)
{
    if (!typeof(T).IsValueType)
    {
        if (Equals(source, default(T))) throw new ArgumentNullException("source");
    }
    return list.Contains(source);
}

I would leave your code as 我会将您的代码保留为

public static bool In<T>(this T source, params T[] list)
{
    if (null == source) throw new ArgumentNullException("source");
    return list.Contains(source);
}

because it is easier to read. 因为它更容易阅读。

On a related note: can source ever be a value type? 相关说明:source可以是值类型吗? If not you could constrain T as T:class . 如果不是,您可以将T限制为T:class

The two methods are basically equivalent. 这两种方法基本上是等效的。

In your original version, if T is a value type, then the test always fails: a value type is never equal to a null pointer. 在您的原始版本中,如果T是一个值类型,则测试将始终失败:值类型从不等于空指针。 Because the condition is always false, the test is optimized away. 由于条件始终为假,因此优化了测试。

In the second version, the test is made explicitly, but the result is exactly the same. 在第二个版本中,测试是显式进行的,但结果完全相同。

I see no reason to prefer one over the other. 我认为没有理由比另一个更喜欢。 Your original is probably slightly faster on value types, but I would add a comment explaining why it works. 您原始的值类型可能稍快一些,但我会添加一条注释来说明其工作原理。

Aside from the performance amelioration, wich is debattable as it makes the code less readable (but not overly so), I'm more concerned by the fact your two methods do not have the same semantic. 除了性能改善之外,它的可争议性还因为它使代码的可读性降低(但不是太过容易理解),我更担心的是,您的两种方法的语义不相同。 There are indeed value types that can be null: Nullable<TValue> , better known as TValue? 确实有一些可以为null的值类型: Nullable<TValue> ,通常称为TValue? .

The following code: 如下代码:

int? nullableInt = null;
nullableInt.In(list);

throws an ArgumentNullException in the first implementation and not in the second one (provided list has been previously correctly initialized). 在第一个实现中而不是第二个实现中抛出ArgumentNullException(提供的列表先前已正确初始化)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM