将泛型与可能是值或引用类型的 null 进行比较
本文关键字:引用类型 null 比较 泛型 | 更新日期: 2023-09-27 18:19:57
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(。虽然我确实想知道:
- 有没有更标准的方法来处理这种情况? 有
- 没有可能由此引起问题?
- 当我进行调用并传入值类型时,底层真正会发生什么?
我故意只检查
null
因为我不想限制 ValueType 等于其default(T)
这是一个很好的见解,但别担心,你已经覆盖在那里了。首先使用==
将 T 与default(T)
进行比较是不合法的;过载分辨率找不到唯一的最佳==
运算符。
当然,您可以与.Equals
进行比较,但是如果接收器为空,您将面临崩溃的风险,而这正是您试图避免的。
有没有更标准的方法来处理这种情况?
不。在这里,与 null 进行比较是正确的做法。
正如 C# 规范在第 7.10.6 节中所说:">即使 T 可以表示值类型,也允许x == null
构造,并且当 T 是值类型时,结果仅定义为 false。
有没有可能由此引起问题?
确定。仅仅因为代码编译并不意味着它具有您想要的语义。编写一些测试。
当我进行调用并传入值类型时,底层真正会发生什么?
这个问题是模棱两可的。让我把它改写成两个问题:
当我使用不可为空的值类型的类型参数调用泛型方法时,到底发生了什么?
抖动在使用该构造的第一次调用时编译方法。当抖动检测到空检查时,它会将其替换为"false",因为它知道任何不可为空的值类型都不会等于空。
当我使用引用类型但参数是结构类型的参数调用泛型方法时,真正发生了什么?例如:
interface IFoo : ISomeInterface<IFoo> {}
struct SFoo : IFoo { whatever }
...
DoFooInternal<IFoo>(new SFoo());
在这种情况下,抖动无法消除空检查,呼叫站点无法避免装箱。SFoo 实例将被装箱,并且将检查对带盒的 SFoo 的引用以查看它是否为 null。
不,不会有任何问题,但如果您希望警告消失,您可以使用以下内容:
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
}