C#でジェネリックメソッドからNULLを返すにはどうすればよいですか?


546

私はこの(ダミー)コードを持つジェネリックメソッドを持っています(はい、IListに述語があることを認識していますが、私のコードはIListを使用していませんが、他のいくつかのコレクションを使用しています。とにかくこれは質問には関係ありません...)

static T FindThing<T>(IList collection, int id) where T : IThing, new()
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return thing;
    }
    return null;  // ERROR: Cannot convert null to type parameter 'T' because it could be a value type. Consider using 'default(T)' instead.
}

これは私にビルドエラーを与えます

「値型の可能性があるため、nullを型パラメーター 'T'に変換できません。代わりに 'default(T)'の使用を検討してください。」

このエラーを回避できますか?


(C#8の)null可能な参照型は、これに対するより良い解決策でしょうか?docs.microsoft.com/en-us/dotnet/csharp/nullable-references戻るnullかどうかにかかわらずのTであるObjectint、またはchar
アレクサンダー-モニカを

回答:


968

2つのオプション:

  • Return は、Tが参照型(またはnull許容値型)、for 、for などの場合に返すことdefault(T)を意味します(デフォルト値の表(C#リファレンス)null0int'\0'char
  • Tをwhere T : class制約付きの参照型に制限し、null通常どおりに返す

3
戻り値の型がクラスではなく列挙型の場合はどうなりますか?Tを指定できません:enum :(
Justin

1
.NETでは、列挙型は整数型を囲む非常に薄い(そしてかなりリークの多い)ラッパーです。慣例では、「デフォルト」の列挙値にゼロを使用します。
Mike Chamberlain

27
これの問題は、このジェネリックメソッドを使用している場合、DatabaseオブジェクトをDbNullからIntに変換するとdefault(T)が返され、Tがintの場合、0が返されることです。この数値が実際に意味があるのであれば、そのフィールドがnullの場合に不良データを迂回することになります。または、より良い例は、DateTimeです。フィールドが「DateClosed」のようなものであり、アカウントがまだ開いているためにnullとして返された場合、実際にはdefault(DateTime)が1/1/0000になり、コンピューターが発明される前にアカウントが閉じられたことを意味します。
Sinaesthetic 2012年

21
@Sinaesthetic:では、Nullable<int>またはNullable<DateTime>その代わりに変換します。null可能ではない型を使用し、null値を表す必要がある場合は、問題が発生するだけです。
Jon Skeet

1
私は同意します、私はそれを取り上げたかっただけです。私がやっていることは、MyMethod <T>();のようなものだと思います。nullを許容しない型とMyMethod <T?>(); null許容型であると想定します。したがって、私のシナリオでは、一時変数を使用してnullをキャッチし、そこから移動することができます。
Sinaesthetic 2012年

84
return default(T);

このリンク: msdn.microsoft.com/en-us/library/xwth0h0d(VS.80).aspxに 理由が説明されています。
Harper Shelby

1
くそー、このキーワードについて知っていれば、かなりの時間を節約できたでしょう。リカルドに感謝します。
Ana Betts、

1
'default'キーワードがより包括的なソリューションであり、数値型および構造体と組み合わせて非参照型を使用できるため、これがより多くの賛成票を獲得していないことに驚いています。受け入れられた回答は問題を解決します(実際に役立ちます)が、戻り値の型をnull許容型/参照型に制限する方法に、より適切に答えます。
スティーブジャクソン

33

制約を調整するだけです。

where T : class

その後、nullを返すことが許可されます。


ありがとう。受け入れられる解決策として2つの回答を選択することはできないため、ジョンスキートの原因を選択します。彼の回答には2つの解決策があります。
edosoft 2008年

@Migolそれはあなたの要件に依存します。多分彼らのプロジェクトはそれを必要としますIDisposable。はい、ほとんどの場合そうである必要はありません。たとえば、System.Stringは実装されていませんIDisposable。回答者はそれを明確にすべきでしたが、それで答えが間違っているわけではありません。:)
ahwm

@MigolそこにIDisposableがあった理由がわかりません。削除されました。
TheSoftwareJedi

13

最初の制約としてクラス制約をジェネリック型に追加します。

static T FindThing<T>(IList collection, int id) where T : class, IThing, new()

ありがとう。受け入れられる解決策として2つの回答を選択することはできないため、ジョンスキートの原因を選択します。彼の回答には2つの解決策があります。
edosoft 2008年

7
  1. オブジェクトがある場合は、型キャストする必要があります

    return (T)(object)(employee);
  2. nullを返す必要がある場合。

    return default(T);

こんにちはuser725388です。最初のオプションを確認してください
Jogi Joseph George

7

以下は、使用できる2つのオプションです

return default(T);

または

where T : class, IThing
 return null;

6

他のオプションは、これを宣言の最後に追加することです。

    where T : class
    where T: IList

これにより、nullを返すことができます。


両方の制約が同じタイプである場合は、タイプを1回言及し、のようにコンマを使用しますwhere T : class, IList。異なるタイプに制約がある場合はwhere、のようにトークンを繰り返しますwhere TFoo : class where TBar : IList
Jeppe Stig Nielsen

3

TheSoftwareJediのソリューションの作品、

また、いくつかの値とnull許容型を使用してアーカイブすることもできます。

static T? FindThing<T>(IList collection, int id) where T : struct, IThing
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return thing;
    }
    return null;
}

1

エラーの推奨事項を取得してください...そして、ユーザーdefault(T)またはのいずれかnew T

そのルートに進んだ場合、コードに比較を追加して、それが有効な一致であることを確認する必要があります。

それ以外の場合は、「一致が見つかりました」の出力パラメーターを検討する可能性があります。


1

Nullable Enumの戻り値の実用的な例を次に示します。

public static TEnum? ParseOptional<TEnum>(this string value) where TEnum : struct
{
    return value == null ? (TEnum?)null : (TEnum) Enum.Parse(typeof(TEnum), value);
}

C#7.3(2018年5月)以降、制約をに改善できますwhere TEnum : struct, Enum。これにより、呼び出し元が列挙型ではない値タイプ(intやaなどDateTime)を誤って指定することがなくなります。
Jeppe Stig Nielsen

0

上記の2つの回答に対する別の選択肢。戻り値の型をに変更するとobject、を返すことができnull、同時にnull以外の戻り値をキャストできます。

static object FindThing<T>(IList collection, int id)
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return (T) thing;
    }
    return null;  // allowed now
}

欠点:メソッドの呼び出し元は、返されたオブジェクトをキャストする必要があります(null以外の場合)。これは、ボクシング->パフォーマンスの低下を意味します。私は正しいですか?
Csharpest

0

完全を期すために、これも実行できることを知っておくとよいでしょう。

return default;

それは同じように返します return default(T);


0

私にとっては、それはそのままの形で機能します。どこに問題があるのですか?

public static T FindThing<T>(this IList collection, int id) where T : IThing, new()
{
    foreach (T thing in collection)
    {
        if (thing.Id == id)
            return thing;
        }
    }

    return null; //work
    return (T)null; //work
    return null as T; //work
    return default(T); //work


}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.