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");
}

ValueTypeaがその と等しくならないように制限したくないため、意図的に null に対してのみチェックしていdefault(T)ます。私のコードはこの方法でコンパイルされ、問題なく動作します (ReSharper は文句を言いますが、CodeAnalysis は文句を言いません)。私は疑問に思いますが:

  • この状況を処理するためのより標準的な方法はありますか?
  • このことから問題が発生する可能性はありますか?
  • 呼び出しを行って値の型を渡すと、内部で実際に何が起こるのでしょうか?
4

2 に答える 2

56

ValueTypenullがそのdefault(T)

これは良い洞察ですが、心配する必要はありません。すでに説明済みです。default(T)そもそも T を使用と比較することは正当ではありません====オーバーロードの解決では、一意の最適な演算子は見つかりません。

もちろん、比較を行うことはできますが.Equals、レシーバーが null の場合にクラッシュするリスクがあります。これは、まさに回避しようとしているものです。

この状況を処理するためのより標準的な方法はありますか?

いいえ、null と比較するのは正しいことです。

C# 仕様のセクション 7.10.6 で述べられているように、「 T が値型を表すことができてもx == null構造体は許可されます。T が値型の場合、結果は単純に false と定義されます。

このことから問題が発生する可能性はありますか?

もちろん。コードがコンパイルされたからといって、意図したセマンティクスがあるとは限りません。いくつかのテストを書きます。

呼び出しを行って値の型を渡すと、内部で実際に何が起こるのでしょうか?

質問があいまいです。2 つの質問に言い換えてみましょう。

Null 非許容値型である型引数を使用してジェネリック メソッドを呼び出すと、内部で実際に何が起こるのでしょうか?

ジッタは、最初の呼び出しでメソッドをその構造でコンパイルします。ジッターがヌル チェックを検出すると、それを「false」に置き換えます。これは、null を許容しない値の型が null と等しくなることはないことがわかっているためです。

型引数が参照型で、引数が構造体型であるジェネリック メソッドを呼び出すと、内部で実際に何が起こるのでしょうか? 例えば:

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

その場合、ジッタはヌル チェックを回避できず、コール サイトはボクシングを回避できません。SFoo インスタンスがボックス化され、ボックス化された SFoo への参照が 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 に答える