77

C# (または Java) で開発されたソフトウェアの多くのバグが NullReferenceException を引き起こすことに気付きました。

「null」が言語に含まれている理由はありますか?

結局のところ、「null」がなければバグはありませんよね?

つまり、言語のどの機能が null なしでは機能しないのでしょうか?

4

25 に答える 25

92

「C#の父」であるアンダース・ヘルスバーグは、Computerworldのインタビューでその点について話しました。

たとえば、型システムでは、値型と参照型が分離されておらず、型のnull可能性がありません。これは少し奇妙に聞こえるか、少し技術的に聞こえるかもしれませんが、C#では、文字列などの参照型をnullにすることはできますが、値型をnullにすることはできません。null不可能な参照型があればいいので、「この文字列はnullになることはないので、ここでnullポインタをヒットできないことをコンパイラに確認してもらいたい」と宣言できます。

私たちのプラットフォームでC#を使用してコーディングしている、今日人々が遭遇するバグの50%は、Javaにも当てはまりますが、おそらくnull参照の例外です。より強力な型システムがあり、「このパラメーターがnullになることはない可能性があります。コンパイラーは、コードの静的分析を行うことにより、呼び出しのたびにそれを確認してください」と言うことができます。そうすれば、バグのクラスを排除することができたはずです。

C#チームの元ソフトウェア設計エンジニアであるCyrus Najmabadi(現在はGoogleで働いています)は、彼のブログでそのテーマについて話し合っています:(1st2nd3rd4th)。null許容型を採用する上での最大の障害は、表記がプログラマーの習慣やコードベースを乱すことであるように思われます。C#プログラムの参照の70%のようなものは、null許容でないものになる可能性があります。

本当にC#でnull許容でない参照型を使用したい場合 は、「!」の使用を許可するC#拡張機能であるSpec#を使用してみてください。null不可能な記号として。

static string AcceptNotNullObject(object! s)
{
    return s.ToString();
}
于 2008-10-07T11:47:23.157 に答える
30

Nullity は、参照型の自然な結果です。参照がある場合は、何らかのオブジェクトを参照するか、null にする必要があります。null を禁止する場合は、すべての変数が null 以外の式で初期化されていることを常に確認する必要があります。それでも、初期化フェーズで変数が読み取られると問題が発生します。

無効の概念を取り除くことをどのように提案しますか?

于 2008-10-07T11:37:56.107 に答える
17

C#のNullは、ほとんどの場合C ++からの持ち越しであり、メモリ内の何も指していなかった(つまり、アドレス0x00)ポインタがありました。このインタビューで、Anders Hejlsbergは、C#にnull許容でない参照型を追加したいと述べています。

Nullは、型システムでも正当な位置を占めますが、ボトム型トップobject型はどこですか)に似ています。lispではボトムタイプはで、Scalaではです。NILNothing

nullなしでC#を設計することは可能でしたが、その場合は、、、、、、nullなどunitialized-value、人々が通常使用する使用法に対して許容できるソリューションを考え出す必要があります。とにかく成功した場合、C++およびJavaプログラマーの間での採用はおそらく少なかったでしょう。少なくとも、C#プログラムにnullポインタ例外がないことを確認するまでは。not-founddefault-valueundefined-valueNone<T>

于 2008-10-07T11:48:03.670 に答える
13

null を削除してもあまり解決しません。init で設定されるほとんどの変数のデフォルト参照が必要です。変数が間違ったオブジェクトを指しているため、null 参照例外の代わりに予期しない動作が発生します。少なくとも null 参照は、予期しない動作を引き起こす代わりに、すばやく失敗します。

この問題の一部を解決する方法については、null オブジェクト パターンを参照してください。

于 2008-10-07T11:39:24.327 に答える
12

結局のところ、「null」がなければバグはありませんよね?

答えはノーです。問題は、C# が null を許可することではなく、NullReferenceException で明らかになるバグがあることです。すでに述べたように、null には、「空」の参照型、または非値 (空/何もない/不明) を示す目的があります。

于 2008-10-07T13:06:26.490 に答える
12

Null は非常に強力な機能です。値がない場合はどうしますか? ヌルだ!

null を返さないという考え方もあれば、常に返すという考え方もあります。たとえば、有効だが空のオブジェクトを返す必要があると言う人もいます。

それが実際に何であるかをより正確に示しているので、私は null を好みます。永続レイヤーからエンティティを取得できない場合は、null が必要です。空の値は必要ありません。しかし、それは私です。

これは、プリミティブで特に便利です。たとえば、true または false を持っているが、セキュリティ フォームで使用されている場合、アクセス許可を許可、拒否、または設定しないことができます。まあ、それをnullに設定しないでください。だから私はboolを使うことができますか?

他にもまだまだいろいろありますが、割愛させていただきます。

于 2008-10-07T11:37:41.787 に答える
5

この質問は、「各参照型 (String.Empty など) または null の既定値を持つ方が良いですか?」と解釈される場合があります。この観点では、null を使用することをお勧めします。

  • 作成するクラスごとにデフォルトのコンストラクターを作成したくありません。
  • このようなデフォルト値に不必要なメモリが割り当てられるのは望ましくありません。
  • 参照が null かどうかのチェックは、値の比較よりもかなり安価です。
  • NullReferanceExceptions の代わりに、検出がより困難なバグがさらに発生する可能性が高くなります。私が何か間違ったことをしている(と仮定して)ことを明確に示すような例外があるのは良いことです。
于 2008-10-07T14:30:15.060 に答える
5

Null は NullPointerExceptions を引き起こしません...

プログラマは NullPointerExceptions を引き起こします。

null がなければ、実際の任意の値を使用して、関数またはメソッドの戻り値が無効であると判断することになります。返された -1 (または何でも) をチェックする必要があります。null を削除しても、怠惰は魔法のように解決されませんが、難読化されるだけです。

于 2008-10-07T13:18:10.587 に答える
3

参照が初期化されていないことを示すのにnullが適している場合があります。これは、いくつかのシナリオでは重要です。

例えば:

MyResource resource;
try
{
  resource = new MyResource();
  //
  // Do some work
  //
}
finally
{
  if (resource != null)
    resource.Close();
}

これは、ほとんどの場合、usingステートメントを使用して実現されます。しかし、パターンはまだ広く使用されています。

NullReferenceException に関しては、このようなエラーの原因は、多くの場合、すべてのパラメーターの有効性をチェックするコーディング標準を実装することで簡単に減らすことができます。プロジェクトの性質にもよりますが、ほとんどの場合、公開されたメンバーのパラメーターをチェックするだけで十分です。パラメーターが予期される範囲内にない場合、使用中のエラー処理パターンに応じて、何らかのArgumentExceptionがスローされるか、エラー結果が返されます。

パラメータ チェック自体はバグを取り除くものではありませんが、発生したバグは、テスト フェーズで簡単に見つけて修正できます。

注として、Anders Hejlsbergは、C# 1.0 仕様の最大の間違いの 1 つとして、null 以外の強制の欠如に言及しており、現在それを含めることは「困難」であると述べています。

静的に強制された null 以外の参照値が非常に重要であるとまだ考えている場合は、spec#言語を確認できます。これは、null 以外の参照が言語の一部である C# の拡張です。これにより、非 null としてマークされた参照に null 参照が割り当てられることはありません。

于 2008-10-07T12:18:12.953 に答える
3

「NullReferenceException」が発生した場合は、存在しないオブジェクトを参照し続けている可能性があります。これは「null」の問題ではなく、存在しないアドレスを指しているコードの問題です。

于 2008-10-07T12:30:02.413 に答える
3

値型と参照型があるため、言語に「Null」が含まれています。副作用かもしれませんが、いいと思います。これにより、メモリを効果的に管理する方法に大きな力が与えられます。

null があるのはなぜですか? ...

値の型は「スタック」に格納され、その値はそのメモリの一部に直接置かれます (つまり、int x = 5 は、その変数のメモリ位置に「5」が含まれることを意味します)。

一方、参照型には、ヒープ上の実際の値を指すスタック上の「ポインター」があります (つまり、文字列 x = "ello" は、スタック上のメモリ ブロックには、ヒープ上の実際の値を指すアドレスのみが含まれることを意味します)。

null 値は、スタック上の値がヒープ上の実際の値を指していないことを意味します。これは空のポインターです。

十分に説明したことを願っています。

于 2008-10-07T11:38:13.893 に答える
2

ある回答では、データベースにnullがあると述べています。それは本当ですが、C#のnullとは大きく異なります。

C#では、nullは何も参照しない参照のマーカーです。

データベースでは、nullは値を含まない値セルのマーカーです。値セルとは、通常、テーブル内の行と列の共通部分を意味しますが、値セルの概念はテーブルを超えて拡張できます。

一見すると、この2つの違いは些細なことのように思われます。しかし、そうではありません。

于 2008-10-07T12:58:37.490 に答える
2

C#/C++/Java/Ruby で利用可能な Null は、何らかの形で今日まで生き残った、あいまいな過去 (Algol) の奇妙さとして最もよく見られます。

次の 2 つの方法で使用します。

  • 参照を初期化せずに宣言する (悪い)。
  • 任意性 (OK) を示す。

ご想像のとおり、1) 一般的な命令型言語で私たちに際限のない問題を引き起こしているものであり、ずっと前に禁止されるべきでした。2) は真に不可欠な機能です。

2) を妨げずに 1) を避ける言語があります。

たとえば、OCamlはそのような言語です。

1 から始まる増加し続ける整数を返す単純な関数:

let counter = ref 0;;
let next_counter_value () = (counter := !counter + 1; !counter);;

そしてオプション性に関して:

type distributed_computation_result = NotYetAvailable | Result of float;;
let print_result r = match r with
    | Result(f) -> Printf.printf "result is %f\n" f
    | NotYetAvailable -> Printf.printf "result not yet available\n";;
于 2008-10-07T12:25:45.590 に答える
1

すでに述べたすべての理由に加えて、まだ作成されていないオブジェクトのプレースホルダーが必要な場合はNULLが必要です。例えば。オブジェクトのペア間に循環参照がある場合、両方を同時にインスタンス化することはできないため、nullが必要です。

class A {
  B fieldb;
}

class B {
  A fielda;
}

A a = new A() // a.fieldb is null
B b = new B() { fielda = a } // b.fielda isnt
a.fieldb = b // now it isnt null anymore

編集:nullなしで機能する言語を引き出すことができるかもしれませんが、それは間違いなくオブジェクト指向言語ではありません。たとえば、プロローグにはnull値がありません。

于 2008-10-07T13:53:29.860 に答える
1

あなたの特定の問題について話すことはできませんが、問題は null の存在ではないようです。データベースに Null が存在するため、アプリケーション レベルでそれを説明する何らかの方法が必要です。それが.netに存在する唯一の理由だとは思いません。でも原因の一つだと思います。

于 2008-10-07T11:36:49.403 に答える
1

I'm surprised no one has talked about databases for their answer. Databases have nullable fields, and any language which will be receiving data from a DB needs to handle that. That means having a null value.

In fact, this is so important that for basic types like int you can make them nullable!

Also consider return values from functions, what if you wanted to have a function divide a couple numbers and the denominator could be 0? The only "correct" answer in such a case would be null. (I know, in such a simple example an exception would likely be a better option... but there can be situations where all values are correct but valid data can produce an invalid or uncalculable answer. Not sure an exception should be used in such cases...)

于 2008-10-07T12:44:05.117 に答える
0

インスタンス変数がオブジェクトへの参照であるオブジェクトを作成する場合、オブジェクト参照を割り当てる前に、この変数にどのような値を提案しますか?

于 2008-10-07T11:53:54.983 に答える
0

私が提案する:

  1. ヌル禁止
  2. ブール値の拡張: True、False、および FileNotFound
于 2008-10-07T13:39:29.577 に答える
0
  • 明示的なヌル化。ただし、必要になることはめったにありません。おそらく、防御的プログラミングの一種と見なすことができます。
  • 1 つのデータ ソース (バイト ストリーム、データベース テーブル) からオブジェクトにフィールドをマッピングするときに、欠落しているフィールドを示すフラグとして (または非 nullable の場合は nullable(T) 構造体) を使用します。可能なすべてのヌル可能フィールドに対してブール値フラグを作成するのは非常に扱いにくくなる可能性があり、そのフィールドの範囲内のすべての値が有効な場合、-1 や 0 などのセンチネル値を使用することは不可能な場合があります。これは、多数のフィールドがある場合に特に便利です。

これらが使用例か悪用例かは主観的ですが、私は時々使用します。

于 2010-11-09T03:34:30.360 に答える
0

一般的に - NullReferenceException は、一部のメソッドが渡されたものが気に入らず、null 参照を返したことを意味します。これは後で使用前に参照をチェックせずに使用されました。

そのメソッドは、null を返す代わりに、より詳細な例外を投げることができました。これは、フェイル ファストモードの考え方に準拠しています。

または、便宜上、メソッドが null を返している可能性があります。これにより、試行する代わりにifを記述して、例外の「オーバーヘッド」を回避できます。

于 2008-10-07T13:45:05.850 に答える
0

4 年遅れて回答して申し訳ありませんが、これまでのところ、元の質問にこのように回答した回答がまったくないことに驚いています。

C# や Javaなどの言語は、C やそれ以前の他の言語と同様に、プログラマーが効率的な方法でポインターを使用して高速で最適化されたコードを記述できるようnullにするためのものです。


  • 低レベルのビュー

最初に少し歴史を。発明された理由nullは、効率化のためです。アセンブリで低レベルのプログラミングを行う場合、抽象化はなく、レジスタに値があり、それらを最大限に活用したいと考えています。0 を有効なポインター値でないと定義することは、オブジェクトを表すか、何も持たないかを表す優れた戦略です。

オプションの値パターンをゼロ メモリ オーバーヘッドで非常に高速に実装できるのに、完全に適切なメモリ ワードの可能な値のほとんどを無駄にする必要はありません。nullこれが非常に便利な理由です。

  • ハイレベル ビュー。

意味的にnullは、プログラミング言語にはまったく必要ありません。たとえば、Haskell のような古典的な関数型言語や ML ファミリでは、null はなく、Maybe または Option という名前の型があります。これらは、生成されたアセンブリ コードがどのように見えるか (コンパイラの仕事になります) にまったく関係なく、オプションの値のより高レベルの概念を表します。

また、コンパイラがより多くのバグをキャッチできるようになり、 NullReferenceException が少なくなるため、これも非常に便利です。

  • それらをまとめる

これらの非常に高度なプログラミング言語とは対照的に、C# と Java では、すべての参照型(ポインターを使用して実装されることになる型の別の名前) に対してnull の値を使用できます。

これは悪いことのように思えるかもしれませんが、プログラマーが内部でどのように動作するかの知識を使用して、より効率的なコードを作成できるという点が良い点です (言語にはガベージ コレクションがあります)。

これが、今日の言語に依然として存在する理由nullです。オプションの値の一般的な概念の必要性と、常に存在する効率の必要性との間のトレードオフです。

于 2013-02-21T20:58:14.883 に答える
0

フレームワークが、新しいアイテムで何をすべきかを指定せずに、あるタイプの配列の作成を許可する場合、そのタイプにはデフォルト値が必要です。可変参照セマンティクス (*) を実装する型の場合、一般的に適切なデフォルト値はありません。非仮想関数呼び出しが null チェックを抑制するように指定する方法がないのは、.NET フレームワークの弱点だと思います。これにより、Length などのプロパティに適切な値を返すことで、String などの不変型を値型として動作させることができます。

(*) VB.NET および C# では、変更可能な参照セマンティクスは、クラスまたは構造体型のいずれかによって実装される可能性があることに注意してください。構造体型は、不変参照を保持するクラス オブジェクトのラップされたインスタンスのプロキシとして機能することにより、可変参照セマンティクスを実装します。

また、クラスが null 非許容の変更可能な値型のセマンティクスを持つ必要があることを指定できると便利です (少なくとも、その型のフィールドをインスタンス化すると、デフォルトのコンストラクターを使用して新しいオブジェクト インスタンスが作成され、その型のフィールドをコピーすると、古いインスタンスをコピーして新しいインスタンスが作成されます (ネストされた値型クラスを再帰的に処理します)。

ただし、このためのフレームワークにどの程度のサポートを組み込む必要があるかは明確ではありません。フレームワーク自体に可変値型、可変参照型、および不変型の違いを認識させることで、外部クラスからの可変型と不変型の混合への参照を保持するクラスが、深く不変なオブジェクトの不要なコピーの作成を効率的に回避できるようになります。

于 2011-12-06T00:03:01.663 に答える
0

null なしでは機能しなかった機能は、「オブジェクトの不在」を表現できることです。

オブジェクトの不在は重要な概念です。オブジェクト指向プログラミングでは、オプションのオブジェクト間の関連付けを表すために必要です。オブジェクト A をオブジェクト B にアタッチすることも、A がオブジェクト B を持たないこともあります。null がなくても、これをエミュレートできます。たとえば、オブジェクトのリストを使用して、B を A に関連付けることができます。そのリストには、1 つの要素 (1 つの B) を含めることも、空にすることもできます。これはやや不便であり、実際には何も解決しません。aobj.blist.first().method()null 参照例外と同様の方法で爆破しようとしているような、B があることを前提とするコード: (ifblistが空の場合、 の動作はblist.first()何ですか?)

リストといえば、 null を使用すると、リンクされたリストを終了できます。Aには、 null の可能性があるListNode別の参照を含めることができます。ListNode木などの他の動的集合構造についても同じことが言えます。Null を使用すると、null である子参照を持つことによってリーフ ノードがマークされる通常のバイナリ ツリーを作成できます。

リストとツリーは null なしで構築できますが、循環するか、無限/遅延でなければなりません。これは、データ構造を設計する際に選択肢を持つことを好むほとんどのプログラマーにとって、おそらく受け入れがたい制約と見なされるでしょう。

null 参照に関連する問題 (バグが原因で誤って発生し、例外を引き起こす null 参照など) は、部分的には静的型システムの結果であり、すべての型に null 値が導入されます: null String、null Integer、null Widget があります。 ...

動的に型指定された言語では、独自の型を持つ単一の null オブジェクトが存在する可能性があります。この結果、null のすべての表現上の利点に加えて、安全性が向上します。たとえば、String パラメーターを受け入れるメソッドを作成すると、パラメーターが null ではなく文字列オブジェクトになることが保証されます。String クラスには null 参照はありません。String であることがわかっているものを null オブジェクトにすることはできません。参照は動的言語では型を持ちません。クラス メンバーや関数パラメーターなどの格納場所には、オブジェクトへの参照となる値が含まれています。そのオブジェクトには参照ではなく型があります。

したがって、これらの言語は、きれいで、多かれ少なかれ数学的に純粋な「ヌル」のモデルを提供し、静的言語はそれをフランケンシュタインの怪物のようなものに変えます。

于 2014-04-01T22:19:01.833 に答える
-3

Null は、すべての OO 言語の必須要件です。オブジェクト参照が割り当てられていないオブジェクト変数は、null である必要があります。

于 2008-10-07T11:37:50.680 に答える