ToUpper()
、StartWith()
などのメソッドを安全に適用する前に、すべての文字列をnull
でテストするのは非常に面倒です。
string
のデフォルト値が空の文字列の場合は、テストする必要はありません。たとえば、int
やdouble
などの他の値型とより一貫性があると感じます。さらにNullable<String>
は意味があります。
では、なぜC#の設計者が文字列のデフォルト値としてnull
を選択したのでしょうか。
注:これは この質問 に関連していますが、それをどうするかではなく、その理由に焦点を当てています。
空の文字列ではなく文字列型のデフォルト値がnullになるのはなぜですか?
string
は参照型であり、すべての参照型のデフォルト値はnull
です。
ToUpper()、StartWith()などのメソッドを安全に適用する前に、すべての文字列をnullでテストするのは非常に面倒です。
それは参照型の振る舞いと一致しています。インスタンスメンバーを呼び出す前に、null参照について適切な場所にチェックを入れる必要があります。
Stringのデフォルト値が空の文字列であれば、テストする必要はなく、たとえばintやdoubleのような他の値型とより一貫性があると感じるでしょう。
デフォルト値をnull
以外の特定の参照型に割り当てると、矛盾したものになります。
さらに
Nullable<String>
は意味があります。
Nullable<T>
は値型を処理します。注目すべきは、Nullable
がオリジナルの 。NETプラットフォーム に導入されていなかったということです。そのため、その規則を変更したなら、たくさんの壊れたコードがあったでしょう。(@ jcolebrand )
string
は参照型なので、Habibは正しいです。
しかしもっと重要なことには、あなたはそれを使う度にdon'tでnull
をチェックしなければなりません。ただし、誰かが関数にArgumentNullException
参照を渡す場合は、おそらくnull
をスローする必要があります。
これが実情です。文字列に対して.ToUpper()
を呼び出そうとすると、フレームワークはNullReferenceException
をスローします。パラメータとして関数に渡されたオブジェクトのプロパティまたはメソッドはすべてnull
に評価される可能性があるため、null
の引数をテストしてもこのケースは発生する可能性があります。
そうは言っても、空の文字列やnullをチェックするのは一般的なことなので、この目的のために String.IsNullOrEmpty()
と String.IsNullOrWhiteSpace()
を用意しています。
あなたは 拡張メソッド (それが価値があるもののために)を書くことができます:
public static string EmptyNull(this string str)
{
return str ?? "";
}
これで安全に動作します。
string str = null;
string upper = str.EmptyNull().ToUpper();
(VS2015および新しいコンパイラの時点で)次のものも使用できます。
string myString = null;
string result = myString?.ToUpper();
文字列resultはnullになります。
空の文字列と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
根本的な理由/問題は、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
があります。 String
をNullableString
またはObject
に変換しようとすると、null以外の場合はValue
が返され、nullの場合はString.Empty
が返されます。 String
にキャストしようとすると、NullableString
インスタンスへのnull以外の参照はValue
に参照を格納します(長さがゼロの場合は、おそらくnullを格納します)。他の参照をキャストすると、例外がスローされます。
文字列をヒープに格納する必要がある場合でも、概念的には、null以外のデフォルト値を持つ値型のように動作しないようにする必要がある理由はありません。値。それらを参照を保持する「通常の」構造として格納することは、それらをタイプ「文字列」として使用するコードにとっては効率的でしたが、「オブジェクト」にキャストするときに間接層と非効率の層を追加しました。 .netがこの晩期に上記の機能のいずれかを追加することを私は予測しませんが、おそらく将来のフレームワークの設計者はそれらを含めることを検討するかもしれません。
文字列変数は参照であり、インスタンスではないためです。
デフォルトでEmptyに初期化することは可能でしたが、それは全面的に多くの矛盾をもたらしました。
なぜC#の設計者が文字列のデフォルト値としてnullを使うことを選んだのでしょうか。
文字列は参照型であるため、参照型はデフォルト値のnull
です。参照型の変数は実際のデータへの参照を格納します。
この場合は、default
キーワードを使用しましょう。
string str = default(string);
str
はstring
であるため、参照型であるため、デフォルト値はnull
です。
int str = (default)(int);
str
はint
なので、値型なので、デフォルト値はzero
です。
string
name__のデフォルト値が空の文字列であれば、テストする必要はありません。
違う!デフォルト値を変更しても、それが参照型であり、誰かが明示的に参照できるという事実は変わりませんsetnull
name__への参照。
さらに
Nullable<String>
は意味があります。
本当のポイントどの参照型に対してもnull
name__を許可しないことをお勧めします。代わりに、その機能にはNullable<TheRefType>
を必要とします。
では、なぜC#の設計者が
null
name__を文字列のデフォルト値として使用することを選択したのでしょうか。
他の参照タイプとの整合性では、なぜnull
name__を参照型として使用できるのでしょうか。これは、Nullable
name__も提供する言語では問題のある設計上の決定ですが、おそらくC言語のように感じます。
おそらく、文字列変数を代入するときに??
演算子を使用したいのであれば、それが役に立ちます。
string str = SomeMethodThatReturnsaString() ?? "";
// if SomeMethodThatReturnsaString() returns a null value, "" is assigned to str.
Stringは不変のオブジェクトです。つまり、値が与えられると、古い値はメモリから消去されずに古い場所に残り、新しい値が新しい場所に置かれます。したがって、String a
のデフォルト値がString.Empty
である場合、最初の値が与えられたときにメモリ内のString.Empty
ブロックが無駄になります。
それはごくわずかに見えますが、デフォルト値のString.Empty
で文字列の大きな配列を初期化するときに問題になる可能性があります。もちろん、これが問題になる場合は、常に可変のStringBuilder
クラスを使用できます。
Stringは参照型であり、参照型のデフォルト値はnullです。
string
name__キーワードは他の値型の宣言とまったく同じように見えるので混乱させるかもしれませんが、実際にはSystem.String
の別名です この質問 。
また、Visual Studioの濃い青と小文字の最初の文字は、それがstruct
name__であると誤解させる可能性があります。
Null許容型は2.0までは登場しませんでした。
NULL可能な型が言語の初めに作られていたら、stringはNULL不可能でstringになっていたでしょうか。 null許容可能だったでしょう。しかし、彼らは後方互換性のためにこれをすることができませんでした。
多くの人がref-typeまたはnot ref typeについて話していますが、stringは普通のクラスの外であり、それを可能にする解決策が見つかったでしょう。