web-dev-qa-db-ja.com

空の文字列ではなく文字列型のデフォルト値がnullになるのはなぜですか?

ToUpper()StartWith()などのメソッドを安全に適用する前に、すべての文字列をnullでテストするのは非常に面倒です。

stringのデフォルト値が空の文字列の場合は、テストする必要はありません。たとえば、intdoubleなどの他の値型とより一貫性があると感じます。さらにNullable<String>は意味があります。

では、なぜC#の設計者が文字列のデフォルト値としてnullを選択したのでしょうか。

注:これは この質問 に関連していますが、それをどうするかではなく、その理由に焦点を当てています。

202
Marcel

空の文字列ではなく文字列型のデフォルト値がnullになるのはなぜですか?

string参照型であり、すべての参照型のデフォルト値はnullです。

ToUpper()、StartWith()などのメソッドを安全に適用する前に、すべての文字列をnullでテストするのは非常に面倒です。

それは参照型の振る舞いと一致しています。インスタンスメンバーを呼び出す前に、null参照について適切な場所にチェックを入れる必要があります。

Stringのデフォルト値が空の文字列であれば、テストする必要はなく、たとえばintやdoubleのような他の値型とより一貫性があると感じるでしょう。

デフォルト値をnull以外の特定の参照型に割り当てると、矛盾したものになります

さらにNullable<String>は意味があります。

Nullable<T> は値型を処理します。注目すべきは、Nullableがオリジナルの 。NETプラットフォーム に導入されていなかったということです。そのため、その規則を変更したなら、たくさんの壊れたコードがあったでしょう。(@ jcolebrand

294
Habib

stringは参照型なので、Habibは正しいです。

しかしもっと重要なことには、あなたはそれを使う度にdon'tnullをチェックしなければなりません。ただし、誰かが関数にArgumentNullException参照を渡す場合は、おそらくnullをスローする必要があります。

これが実情です。文字列に対して.ToUpper()を呼び出そうとすると、フレームワークはNullReferenceExceptionをスローします。パラメータとして関数に渡されたオブジェクトのプロパティまたはメソッドはすべてnullに評価される可能性があるため、nullの引数をテストしてもこのケースは発生する可能性があります。

そうは言っても、空の文字列やnullをチェックするのは一般的なことなので、この目的のために String.IsNullOrEmpty()String.IsNullOrWhiteSpace() を用意しています。

40
Dave Markle

あなたは 拡張メソッド (それが価値があるもののために)を書くことができます:

public static string EmptyNull(this string str)
{
    return str ?? "";
}

これで安全に動作します。

string str = null;
string upper = str.EmptyNull().ToUpper();
23
Tim Schmelter

(VS2015および新しいコンパイラの時点で)次のものも使用できます。

string myString = null;
string result = myString?.ToUpper();

文字列resultはnullになります。

15
russelrillema

空の文字列とnullは基本的に異なります。 nullは値がないことを表し、空の文字列は空の値を表します。

変数の「値」、この場合は空の文字列についての仮定をするプログラミング言語は、null参照問題を引き起こさない他の値で文字列を初期化するのと同じくらい良いでしょう。

また、その文字列変数のハンドルをアプリケーションの他の部分に渡した場合、そのコードには、意図的に空白値を渡したのか、その変数の値を入力するのを忘れたのかを検証する方法はありません。

これが問題となるもう1つの機会は、文字列が何らかの関数からの戻り値である場合です。 stringは参照型であり、技術的にnullとemptyの両方の値を持つことができるため、関数は技術的にnullまたは空を返すこともできます(それを止めるものは何もありません)。さて、 "値が存在しない"という2つの概念、すなわち空の文字列とnullがあるので、この関数を消費するすべてのコードは2つのチェックをしなければならないでしょう。一方は空、もう一方はnullです。

一言で言えば、単一の状態に対して1つの表現だけを持つことは常に良いことです。空とnullについてのより広い議論については、以下のリンクを見てください。

https://softwareengineering.stackexchange.com/questions/32578/sql-empty-string-vs-null-value

ユーザー入力を処理するときはNULLと空の対比

14
Nerrve

根本的な理由/問題は、CLS仕様の設計者(言語が.netと対話する方法を定義する)が、呼び出し側がパフォーマンスを行わずにクラスメンバーがcallvirtを介してではなく直接呼び出されることを指定できる方法を定義しなかったことです。 null参照チェックまた、「通常の」ボクシングの対象にならないような構造を定義することを意味するものでもありませんでした。

CLS仕様がそのような手段を定義していれば、.netが一貫してCommon Object Model(COM)によって確立されたリードに従うことが可能であり、その下でnull文字列参照は空の文字列と意味的に同等と見なされます。デフォルト値を同様に定義するための値セマンティクスを持つはずのユーザー定義の不変クラス型。基本的に、起こることはStringの各メンバに対して、例えばLength[InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }のようなものとして書かれます。このアプローチは値のように振る舞うべきものに対して非常にいい意味論を提供したでしょう、しかし実装上の問題のためにヒープに保存される必要があります。このアプローチの最大の難点は、そのような型とObjectの間の変換の意味論が少し曖昧になる可能性があることです。

代わりのアプローチは、Objectから継承しないで、代わりにカスタムのボクシングおよびアンボックス化操作(他のクラス型へ/から変換する)を持つ特別な構造体型の定義を許可することでした。そのようなアプローチの下では、文字列のように振る舞うクラス型NullableStringと、型がStringの単一プライベートフィールドValueを保持するカスタムボックス構造体Stringがあります。 StringNullableStringまたはObjectに変換しようとすると、null以外の場合はValueが返され、nullの場合はString.Emptyが返されます。 Stringにキャストしようとすると、NullableStringインスタンスへのnull以外の参照はValueに参照を格納します(長さがゼロの場合は、おそらくnullを格納します)。他の参照をキャストすると、例外がスローされます。

文字列をヒープに格納する必要がある場合でも、概念的には、null以外のデフォルト値を持つ値型のように動作しないようにする必要がある理由はありません。値。それらを参照を保持する「通常の」構造として格納することは、それらをタイプ「文字列」として使用するコードにとっては効率的でしたが、「オブジェクト」にキャストするときに間接層と非効率の層を追加しました。 .netがこの晩期に上記の機能のいずれかを追加することを私は予測しませんが、おそらく将来のフレームワークの設計者はそれらを含めることを検討するかもしれません。

7
supercat

文字列変数は参照であり、インスタンスではないためです。

デフォルトでEmptyに初期化することは可能でしたが、それは全面的に多くの矛盾をもたらしました。

5
Henk Holterman

なぜC#の設計者が文字列のデフォルト値としてnullを使うことを選んだのでしょうか。

文字列は参照型であるため、参照型はデフォルト値のnullです。参照型の変数は実際のデータへの参照を格納します。

この場合は、defaultキーワードを使用しましょう。

string str = default(string); 

strstringであるため、参照型であるため、デフォルト値はnullです。

int str = (default)(int);

strintなので、値型なので、デフォルト値はzeroです。

5
Soner Gönül

stringname__のデフォルト値が空の文字列であれば、テストする必要はありません。

違う!デフォルト値を変更しても、それが参照型であり、誰かが明示的に参照できるという事実は変わりませんsetnullname__への参照。

さらにNullable<String>は意味があります。

本当のポイントどの参照型に対してもnullname__を許可しないことをお勧めします。代わりに、その機能にはNullable<TheRefType>を必要とします。

では、なぜC#の設計者がnullname__を文字列のデフォルト値として使用することを選択したのでしょうか。

他の参照タイプとの整合性では、なぜnullname__を参照型として使用できるのでしょうか。これは、Nullablename__も提供する言語では問題のある設計上の決定ですが、おそらくC言語のように感じます。

4
Dan Burton

おそらく、文字列変数を代入するときに??演算子を使用したいのであれば、それが役に立ちます。

string str = SomeMethodThatReturnsaString() ?? "";
// if SomeMethodThatReturnsaString() returns a null value, "" is assigned to str.
3
Amen JLILI

Stringは不変のオブジェクトです。つまり、値が与えられると、古い値はメモリから消去されずに古い場所に残り、新しい値が新しい場所に置かれます。したがって、String aのデフォルト値がString.Emptyである場合、最初の値が与えられたときにメモリ内のString.Emptyブロックが無駄になります。

それはごくわずかに見えますが、デフォルト値のString.Emptyで文字列の大きな配列を初期化するときに問題になる可能性があります。もちろん、これが問題になる場合は、常に可変のStringBuilderクラスを使用できます。

2
djv

Stringは参照型であり、参照型のデフォルト値はnullです。

2
Akshay

stringname__キーワードは他の値型の宣言とまったく同じように見えるので混乱させるかもしれませんが、実際にはSystem.Stringの別名です この質問
また、Visual Studioの濃い青と小文字の最初の文字は、それがstructname__であると誤解させる可能性があります。

0

Null許容型は2.0までは登場しませんでした。

NULL可能な型が言語の初めに作られていたら、stringはNULL不可能でstringになっていたでしょうか。 null許容可能だったでしょう。しかし、彼らは後方互換性のためにこれをすることができませんでした。

多くの人がref-typeまたはnot ref typeについて話していますが、stringは普通のクラスの外であり、それを可能にする解決策が見つかったでしょう。

0
Thomas Koelle