将泛型与null相比较,可以是值或引用类型?

时间:2012-01-11 16:52:51

标签: c# generics null value-type null-check

public void DoFoo<T>(T foo) where T : ISomeInterface<T>
{
    //possible compare of value type with 'null'.
    if (foo == null) throw new ArgumentNullException("foo");
}

我故意只检查null,因为我不想限制ValueType等于default(T)。我的代码以这种方式编译和工作(ReSharper抱怨,但不是CodeAnalysis)。虽然我确实想知道:

  • 是否有更多的标准方式来处理这种情况?
  • 这是否有可能引发问题?
  • 当我打电话并传递值类型时,真正发生了什么?

2 个答案:

答案 0 :(得分:53)

  

我故意只检查null,因为我不想限制ValueType等于default(T)

这是一个很好的见解,但不要担心,你已经被覆盖了。首先使用default(T)将T与==进行比较是不合法的;重载解析将找不到唯一的最佳==运算符。

当然,您可以与.Equals进行比较,但如果接收方为空,则会冒着崩溃的风险,这正是您试图避免的。

  

有没有更标准的方法来处理这种情况?

没有。比较null是正确的做法。

正如C#规范在第7.10.6节中所述:“即使T可以表示值类型,也允许x == null构造,并且当T是值时,结果简单地定义为false类型。

  

有没有可能出现这个问题?

不确定。仅仅因为代码编译并不意味着它具有您想要的语义。写一些测试。

  

当我拨打电话并传递值类型时,真正发生了什么?

问题含糊不清。让我把它改成两个问题:

  

当我使用类型参数作为非可空值类型调用泛型方法时,真正发生了什么?

抖动在第一次调用时使用该构造编译方法。当抖动检测到空检查时,它将其替换为“false”,因为它知道没有非可空值类型将等于null。

  

当我使用作为引用类型的类型参数但是作为结构类型的参数的泛型方法调用时,真正发生了什么?例如:

interface IFoo : ISomeInterface<IFoo> {}
struct SFoo : IFoo { whatever }
...
DoFooInternal<IFoo>(new SFoo());

在这种情况下,抖动不能忽略空检查,并且呼叫站点无法避免装箱。将对SFoo实例进行装箱,并检查对盒装SFoo的引用是否为空。

答案 1 :(得分:11)

不,不会有任何问题,但如果您希望警告消失,您可以使用以下内容:

public void DoFoo<T>(T foo) where T : ISomeInterface<T>
{
    if (ReferenceEquals(foo, null)) throw new ArgumentNullException("foo");
}

或者你也可以这样做:

// when calling this with an actual T parameter, you have to either specify the type
// explicitly or cast the parameter to T?.
public void DoFoo<T>(T? foo) where T : struct, ISomeInterface<T>
{
    if (foo == null)
    {
        // throw...
    }

    DoFooInternal(foo.Value);
}

public void DoFoo<T>(T foo) where T : class, ISomeInterface<T>
{
    if (foo == null)
    {
        // throw...
    }

    DoFooInternal(foo); 
}

private void DoFooInternal<T>(T foo) where T : ISomeInterface<T>
{
    // actual implementation
}