将泛型与可能是值或引用类型的 null 进行比较?

发布于 2024-12-25 23:59:28 字数 448 浏览 1 评论 0原文

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 不抱怨)。尽管我确实想知道:

  • 是否有更标准的方法来处理这种情况?
  • 这是否有可能引发问题?
  • 当我进行调用并传入值类型时,幕后到底发生了什么?
public void DoFoo<T>(T foo) where T : ISomeInterface<T>
{
    //possible compare of value type with 'null'.
    if (foo == null) throw new ArgumentNullException("foo");
}

I'm purposely only checking against null because I don't want to restrict a ValueType from being equal to its default(T). My code compiles and works just fine this way (ReSharper complains, but not CodeAnalysis). Though I do wonder:

  • Is there a more standard way to handle this situation?
  • Is there any chance of an issue arrising from this?
  • What truly happens under the hood when I make a call and pass in a value type?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

肤浅与狂妄 2025-01-01 23:59:29

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

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
}

No, there won't be any problems, but if you want the warning to disappear, you can use the following:

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

Alternatively you can do something like this:

// 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
}
太阳男子 2025-01-01 23:59:28

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

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

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

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

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

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

这是否有可能引发问题?

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

当我进行调用并传入值类型时,幕后到底发生了什么?

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

当我使用不可为空值类型的类型参数调用泛型方法时,幕后到底发生了什么?

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

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

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

在这种情况下,抖动无法消除空检查,并且调用站点无法避免装箱。 SFoo 实例将被装箱,并且将检查对装箱的 SFoo 的引用是否为 null。

I'm purposely only checking against null because I don't want to restrict a ValueType from being equal to its default(T)

That is a good insight, but don't worry, you are already covered there. It is not legal to compare a T against default(T) using == in the first place; overload resolution will not find a unique best == operator.

Of course, you could do the comparison with .Equals but then you run the risk of crashing if the receiver is null, which is precisely what you are attempting to avoid.

Is there a more standard way to handle this situation?

No. Comparing to null is the right thing to do here.

As the C# specification says in section 7.10.6: "The x == null construct is permitted even though T could represent a value type, and the result is simply defined to be false when T is a value type."

Is there any chance of an issue arrising from this?

Sure. Just because code compiles does not mean that it has the semantics you intend. Write some tests.

What truly happens under the hood when I make a call and pass in a value type?

The question is ambiguous. Let me rephrase it into two questions:

What truly happens under the hood when I make a call on the generic method with a type argument that is a non-nullable value type?

The jitter compiles the method on the first invocation with that construction. When the jitter detects the null check, it replaces it with "false" because it knows that no non-nullable value type will ever be equal to null.

What truly happens under the hood when I make a call on the generic method with a type argument that is a reference type but an argument that is a struct type? For example:

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文