C#で「nullをスロー」できるのはなぜですか?


85

特に複雑な例外処理コードを書いているときに、誰かが尋ねました。例外オブジェクトがnullでないことを確認する必要はありませんか?もちろんそうではないと言いましたが、試してみることにしました。どうやら、nullをスローすることはできますが、それでもどこかで例外になります。

なぜこれが許可されるのですか?

throw null;

このスニペットでは、ありがたいことに「ex」はnullではありませんが、nullになる可能性はありますか?

try
{
  throw null;
}
catch (Exception ex)
{
  //can ex ever be null?

  //thankfully, it isn't null, but is
  //ex is System.NullReferenceException
}

8
自分のスローの上に.NET例外(null参照)がスローされていないことを確認しますか?
micahtan 2010

4
コンパイラは少なくとも、直接「nullをスロー」しようとすることについて警告を発すると思います。
アンディホワイト

いいえ、わかりません。フレームワークは私のオブジェクトで何かをしようとしている可能性があり、それがnullの場合、フレームワークは「Null参照例外」をスローします..しかし、最終的には、「ex」が決してnullにならないようにしたい
quip

1
@Andy:警告は、言語内の他の状況では意味があるかもしれませんが、そもそも例外をスローすることthrow目的とするステートメントの場合、警告はあまり価値がありません。
Mehrdad Afshari 2010

@ Mehrdad-ええ、でも、開発者が意図的に「nullをスロー」して、NullReferenceExceptionを見たいと思うかどうかは疑問です。ifステートメントでの誤った=比較のように、完全なビルドエラーである可能性があります。
アンディホワイト

回答:


96

言語仕様はSystem.Exceptionそこで型の式を期待し(したがって、nullそのコンテキストで有効です)、この式がnull以外になることを制限しないためです。一般に、その式の値がそうであるかどうかを検出する方法はありnullません。停止性問題を解決する必要があります。nullとにかく、ランタイムはケースに対処する必要があります。見る:

Exception ex = null;
if (conditionThatDependsOnSomeInput) 
    ex = new Exception();
throw ex; 

もちろん、nullリテラルをスローする特定のケースを無効にすることはできますが、それはあまり役に立ちません。それでは、なぜ仕様スペースを無駄にし、一貫性を減らしてほとんど利益がないのでしょうか。

免責事項(Eric Lippertに叩かれる前):これはこの設計上の決定の背後にある理由についての私自身の推測です。もちろん、私はデザインミーティングに参加していません;)


2番目の質問に対する答えは、catch句内でキャッチされた式変数がnullになる可能性があるかどうかです。C#仕様では、他の言語でnull例外が伝播されるかどうかについては言及されていませんが、例外が伝播される方法は定義されています。

キャッチ句がある場合は、出現順に調べて、例外に適したハンドラーを見つけます。例外タイプまたは例外タイプの基本タイプを指定する最初のcatch句は、一致と見なされます。一般的なcatch句は、すべての例外タイプに一致すると見なされます。[...]

の場合null、太字のステートメントは誤りです。したがって、純粋にC#仕様の内容に基づいていますが、基になるランタイムがnullをスローしないとは言えませんが、その場合でも、genericcatch {}句によってのみ処理されると確信できます。

CLIでのC#実装については、ECMA335仕様を参照できます。このドキュメントでは、CLIが内部でスローするすべての例外(いずれもnull)を定義し、ユーザー定義の例外オブジェクトがthrow命令によってスローされることに言及しています。その命令の説明は、C#throwステートメントと実質的に同じです(オブジェクトのタイプをに制限しないことを除いてSystem.Exception):

説明:

このthrow命令は、例外オブジェクト(type O)をスタックにスローし、スタックを空にします。例外メカニズムの詳細については、パーティションIを参照してください。
[注:CLIはすべてのオブジェクトのスローを許可しますが、CLSは、言語の相互運用性に使用される特定の例外クラスを記述します。エンドノート]

例外:

System.NullReferenceException場合にスローされてobjいますnull

正しさ:

正しいCILは、オブジェクトが常にnullまたはオブジェクト参照(つまり、タイプO)であることを保証します。

キャッチされた例外は決してないと結論付けるには、これらで十分だと思いますnull


できる最善の方法は、などの明示的なフレーズを禁止することだと思いますthrow null;
frustratedWithFormsDesigner 2010

7
実際には、System.Exceptionから継承しないオブジェクトが(CLRで)スローされる可能性は完全にあります。私の知る限り、C#で実行することはできませんが、IL、C ++ / CLRなどを介して実行できます。詳細については、msdn.microsoft.com / en-us / library /ms404228.aspxを参照してください
technophile 2010

@technophile:はい。私はここで言語について話している。C#では、他のタイプの例外をスローすることはできませんが、ジェネリックcatch { }句を使用してそれをキャッチすることはできます。
Mehrdad Afshari 2010

1
コンパイラthrowが引数がnull値である可能性のあるaを受け入れることを拒否することは意味がありませんが、それthrow nullが合法である必要があることを意味するわけではありません。コンパイラーは、throw引数が識別可能なクラス型を持っていると主張することができます。のような式(System.InvalidOperationException)nullはコンパイル時に有効である必要があります(実行するとNullReferenceException)が発生しますが、型なしnullが受け入れられる必要があるという意味ではありません。
スーパーキャット2012年

@supercat:それは本当ですが、この場合のnullは、暗黙的にExceptionとして入力されます(Exceptionが必要なコンテキストで使用されるため)。nullは実行時に無効であるため、NullReferenceExceptionがスローされます(Anon。の回答に記載されています)。スローされる例外は、「throw」ステートメントの例外ではありません。
John B. Lambe 2018年

29

どうやら、nullをスローすることはできますが、それでもどこかで例外になります。

nullオブジェクトをスローしようとすると、(完全に無関係な)Null参照例外が発生します。

投げるnullことが許可されている理由を尋ねるのは、なぜこれを行うことが許可されているのかを尋ねるようなものです。

object o = null;
o.ToString();

5

ここから撮影:

この式をC#コードで使用すると、NullReferenceExceptionがスローされます。これは、throwステートメントが単一のパラメーターとしてExceptionタイプのオブジェクトを必要とするためです。しかし、私の例では、このオブジェクト自体がnullです。


5

スローがそれを検出してNullReferenceExceptionに変換するため、C#でnullをスローすることはできない場合がありますが、nullを受信することは可能です...たまたまそれを受信して​​いるため、キャッチが発生します( 'ex'がnullになることを期待している)null参照例外が発生すると、アプリが停止します(これが最後のキャッチだったため)。

したがって、C#からnullをスローすることはできませんが、netherworldはnullをスローできるため、最も外側のcatch(Exception ex)はそれを受け取る準備をする方がよいでしょう。参考までに。


3
面白い。あなたはいくつかのコードを投稿することができますか、あるいはこれを行っているあなたの深さの下にあるものを逃れることができますか?
Peter Lillevold 2012

それがCLRのバグであるかどうかはわかりません.... NETの内部で何がnullをスローしたのか、そしてコールスタックやその他の手がかりで例外が発生しない理由を突き止めるのは困難です。私の最も外側のキャッチが、使用する前にnullException引数をチェックするようになったことを知っています。
ブライアンケネディ

3
これはCLRのバグか、検証できないコードが原因であると思われます。正しいCILは、null以外のオブジェクト、またはnull(NullReferenceExceptionになります)のいずれかのみをスローします。
デミ

null例外を返すサービスも使用しています。null例外がどのようにスローされているかを理解したことがありますか?
themiDdlest 2016

2

おそらくあなたはできないと思います-nullをスローしようとすると、それはできません。そのため、null参照例外をスローするというエラーの場合に必要なことを実行します。したがって、実際にnullをスローしているのではなく、nullをスローできていないため、スローが発生します。


2

「..ありがたいことに 'ex'はnullではありませんが、nullになる可能性はありますか?」と答えようとしています。

間違いなくnullの例外をスローすることはできないため、catch句もnullの例外をキャッチする必要はありません。したがって、exがnullになることはありません。

私は今、この質問が実際にすでに尋ねられているのを見る。


@Brian Kennedyは、上記のコメントで、nullをキャッチできると言っています。
ProfK 2012

@ Tvde1-ここでの違いは、はい、実行できるということだと思いますthrow null。ただし、nullの例外スローされません。むしろ、throw nullnull参照でメソッドを呼び出そうとするため、これにより、ランタイムはNullReferenceException。のnull以外のインスタンスをスローします。
PeterLillevold19年

1

例外には、例外がスローされる場所に関する詳細が含まれていることに注意してください。コンストラクターはそれがどこにスローされるかわからないので、throwメソッドがthrowが行われるポイントでそれらの詳細をオブジェクトに注入することは意味があります。つまり、CLRがデータをnullに挿入しようとしているため、NullReferenceExceptionがトリガーされます。

これがまさに起こっていることであるかどうかはわかりませんが、それは現象を説明しています。

これが真であると仮定すると(そして、nullをスローするよりもexをnullにするためのより良い方法は考えられません;)、それはexがnullになる可能性がないことを意味します。


0

古いc#では:

この構文を検討してください。

public void Add<T> ( T item ) => throw (hashSet.Add ( item ) ? null : new Exception ( "The item already exists" ));

私はそれがこれよりずっと短いと思います:

public void Add<T> ( T item )
{
    if (!hashSet.Add ( item ))
        throw new Exception ( "The item already exists" );
}

これは私たちに何を伝えますか?
bornfromanegg

ちなみに、shortの定義が何であるかはわかりませんが、2番目の例には含まれる文字が少なくなっています。
bornfromanegg

@bornfromanegg no 1stはインライン化されているので、間違いなく短くなります。私が言おうとしていたのは、条件によってはエラーが発生する可能性があるということです。従来、uは2番目の例を好みますが、「throw null」は何もスローしないため、uは2番目の例を1番目の例のようにインライン化できます。また、最初の例では2番目より8文字少ない
Arutyun Enfendzhyan

「thrownull」はNullReference例外をスローします。つまり、最初の例では常に例外スローされます。
bornfromanegg

残念ながら、今はそうです。しかし、以前はそうではありませんでした
Arutyun Enfendzhyan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.