C#では、nullオブジェクトの拡張メソッドを呼び出すとどうなりますか?


329

メソッドはnull値で呼び出されますか、それともnull参照例外が発生しますか?

MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?

この場合、「this」パラメーターをチェックしてnullを調べる必要はありませんか?


もちろん、このエラーをスローするASP.NET MVCを扱っている場合を除きますCannot perform runtime binding on a null reference
Mrchief

回答:


387

これは正常に動作します(例外はありません)。拡張メソッドは仮想呼び出しを使用しない(つまり、「callvirt」ではなく「call」il命令を使用する)ため、拡張メソッドで自分で記述しない限り、nullチェックはありません。これは実際にはいくつかの場合に役立ちます。

public static bool IsNullOrEmpty(this string value)
{
    return string.IsNullOrEmpty(value);
}
public static void ThrowIfNull<T>(this T obj, string parameterName)
        where T : class
{
    if(obj == null) throw new ArgumentNullException(parameterName);
}

基本的に、静的呼び出しの呼び出しは非常にリテラルです-つまり

string s = ...
if(s.IsNullOrEmpty()) {...}

になる:

string s = ...
if(YourExtensionClass.IsNullOrEmpty(s)) {...}

nullチェックがないことは明らかです。


1
マーク、あなたは「仮想」呼び出しについて話していますが、インスタンスメソッドの非仮想呼び出しについても同じことが言えます。ここでの「仮想」という言葉は間違っていると思います。
Konrad Rudolph、

6
@Konrad:状況によって異なります。C#コンパイラは通常、非仮想メソッドに対してもcallvirtを使用して、正確にnullチェックを取得します。
ジョンスキート、

私はcallとcallvirt il命令の違いに言及していました。1つの編集で、私は実際に2つのOpcodesページをhref表示しようとしましたが、エディターはリンクに失敗しました...
Marc Gravell

2
この拡張メソッドの使用が実際にどのように役立つかはわかりません。それができるからといって、それが正しいことを意味するわけではありません。また、Binary Worrierが後述するように、控えめに言っても、私には異常のように見えます。
トラップ

3
@Trap:この機能は、関数型プログラミングに慣れている場合に最適です。
ロイティンカー

50

Marc Gravellからの正解への追加。

this引数がnullであることが明らかな場合、コンパイラから警告が表示される可能性があります。

default(string).MyExtension();

実行時には正常に機能しますが、警告が表示され"Expression will always cause a System.NullReferenceException, because the default value of string is null"ます。


32
「常にSystem.NullReferenceExceptionが発生する」と警告するのはなぜですか。実際、いつになるのでしょうか?
tpower 2009年

46
幸いにも、プログラマーは警告ではなくエラーのみを気にします:p
JulianR

7
@JulianR:はい。私たちのリリースビルド構成では、警告をエラーとして扱います。したがって、それは機能しません。
Stefan Steinegger、2009年

8
メモありがとうございます。これをバグデータベースで取得し、C#4.0で修正できるかどうかを確認します。(約束はありません。これは非現実的なコーナーケースであり、単なる警告であるため、修正を急ぐ可能性があります。)
Eric Lippert

3
@Stefan:これはバグであり、「真の」警告ではないため、#pragmaステートメントを使用して警告を抑制し、コードがリリースビルドを通過するようにすることができます。
Martin RL

25

すでに発見したように、拡張メソッドは単に美化された静的メソッドであるためnullNullReferenceExceptionスローされずに参照が渡されて呼び出されます。ただし、呼び出し元にとってはインスタンスメソッドのように見えるため、それらもそのように動作する必要があります。次に、ほとんどの場合、thisパラメータを確認し、そうであれば例外をスローしますnullnull以下の例のように、メソッドが明示的に値を処理し、その名前が適切に値を示している場合は、これを行わなくてもかまいません。

public static class StringNullExtensions { 
  public static bool IsNullOrEmpty(this string s) { 
    return string.IsNullOrEmpty(s); 
  } 
  public static bool IsNullOrBlank(this string s) { 
    return s == null || s.Trim().Length == 0; 
  } 
}

私はこれについても前にブログ記事を書いことがあります。


3
@Marc Gravellの回答で説明されている使用法を好む一方で、それは正しいと私(およびよく書かれた)にも意味があるため、これに投票しました。
qxotk 2018

17

nullが拡張メソッドに渡されます。

メソッドがnullかどうかをチェックせずにオブジェクトにアクセスしようとすると、はい、例外がスローされます。

ここの男は「IsNull」と「IsNotNull」拡張メソッドを書いて、参照がnullを渡されたかどうかをチェックします。個人的には、これは異常であり、一日のうちに見られるべきではなかったと思いますが、完全に有効なC#です。


18
実際、私にとっては、死体に「Are you alive」と聞いて「no」と答えるようなものです。死体はどんな質問にも答えることができず、nullオブジェクトのメソッドを「呼び出す」こともできません。
Binary Worrier 2009年

14
Binary Worrierのロジックには同意しません。nullrefを気にすることなく拡張機能を呼び出すことができるので便利ですが、アナロジーコメディの値は+1です。-)
Tim Abell

10
実際、誰かが死んでいるかどうかわからないことがあるので、それでも尋ねると、その人は「いや、ただ目を閉じて休んでいる」と答えるかもしれません
nurchi

7
複数の操作(3+など)をチェーンする必要がある場合、(副作用がないと仮定して)退屈なボイラープレートのnullチェックコードの数行を、「null-safe」拡張メソッドを備えたエレガントなチェーンの1行に変換できます。(提案された「。?」演算子と同様ですが、確かにそれほどエレガントではありません。)拡張子が「nullセーフ」であることが明らかでない場合は、通常、メソッドの前に「Safe」を付けます。メソッドでは、その名前は "SafeCopy"であり、引数がnullの場合はnullを返します。
AnorZaken

3
私は@BinaryWorrierの回答で一生懸命に笑いましたはははは自分が死体か死んでいないかを確認するために体を蹴っているのを見ましたチェックが私の中にあり、動くかどうかを積極的に蹴っていました。だから体は死んでいるかどうかはわからない、WHOはチェックして知っている。死んだかどうかを伝える方法を体に「プラグイン」できると主張することができ、それは私の意見では拡張機能が対象です。
Zorkind

7

他の人が指摘したように、null参照で拡張メソッドを呼び出すと、this引数がnullになり、特別なことは何も起こりません。これにより、拡張メソッドを使用してガード句を作成するというアイデアが生まれます。

例としてこの記事を読むことができます:循環的複雑さを軽減する方法:ガード節短いバージョンはこれです:

public static class StringExtensions
{
    public static void AssertNonEmpty(this string value, string paramName)
    {
        if (string.IsNullOrEmpty(value))
            throw new ArgumentException("Value must be a non-empty string.", paramName);
    }
}

これは、null参照で呼び出すことができる文字列クラス拡張メソッドです。

((string)null).AssertNonEmpty("null");

ランタイムがnull参照で拡張メソッドを正常に呼び出すため、呼び出しは正常に機能します。次に、この拡張メソッドを使用して、面倒な構文なしでガード句を実装できます。

    public IRegisteredUser RegisterUser(string userName, string referrerName)
    {

        userName.AssertNonEmpty("userName");
        referrerName.AssertNonEmpty("referrerName");

        ...

    }


-1

あなたがあなたの中で読みやすく、垂直であることを望むとき、いくつかの黄金律があります。

  • Eiffelから言った1つの価値は、メソッドにカプセル化された特定のコードが一部の入力に対して機能するはずであり、そのコードはいくつかの前提条件を満たし、期待される出力を保証する場合に機能することです

あなたの場合-DesignByContractが壊れています... nullインスタンスでいくつかのロジックを実行します。

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