43
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 因为我不想限制 aValueType等于它的default(T)。我的代码以这种方式编译和工作得很好(ReSharper 抱怨,但不是 CodeAnalysis)。虽然我确实想知道:

  • 有没有更标准的方法来处理这种情况?
  • 有没有可能由此引发问题?
  • 当我拨打电话并传入值类型时,真正发生了什么?
4

2 回答 2

56

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

这是一个很好的洞察力,但别担心,你已经被覆盖了。default(T)首先将 T 与using进行比较是不合法的==;重载决议不会找到唯一的最佳==运算符。

当然,您可以进行比较,.Equals但如果接收器为空,您将面临崩溃的风险,这正是您试图避免的。

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

不,与 null 比较是正确的做法。

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

有没有可能由此引发问题?

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

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

这个问题是模棱两可的。让我把它改写成两个问题:

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

jitter 在第一次调用时使用该构造编译方法。当抖动检测到空值检查时,它会将其替换为“false”,因为它知道任何不可为空的值类型永远不会等于空值。

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

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

In that case the jitter cannot elide the null check and the call site cannot avoid the boxing. The SFoo instance will be boxed, and the reference to the boxed SFoo will be checked to see if it is null.

于 2012-01-11T17:59:38.100 回答
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
}
于 2012-01-11T17:17:25.993 回答