Null文字列を連結することは有効だが「null.ToString()」を呼び出さないことがなぜ有効なのですか?


126

これは有効なC#コードです

var bob = "abc" + null + null + null + "123";  // abc123

これは有効なC#コードではありません

var wtf = null.ToString(); // compiler error

最初のステートメントが有効なのはなぜですか?


97
あなたnull.ToString()に名前が付けられているのは独特ですwtf。なぜ驚いたのですか?そもそもそれを呼び出すものがない場合、インスタンスメソッドを呼び出すことはできません。
BoltClock

11
@BoltClock:もちろん、nullインスタンスでインスタンスメソッドを呼び出すことも可能です。C#では不可能ですが、CLRでは非常に有効です:)
leppie '30年

1
String.Concatに関する答えはほぼ正しいです。実際、問題の特定の例は定数の折りたたみの 1つであり、最初の行のnullはコンパイラによって削除されます。つまり、存在しないため、実行時に評価されることはありません。コンパイラはそれらを消去しました。詳細については、ここで文字列の連結と定数の折りたたみのすべてのルールについて少しモノローグを書きました:stackoverflow.com/questions/9132338/…
Chris Shain、2012年

77
文字列には何も追加できませんが、何もない文字列を作成することはできません。
RGB

2番目のステートメントは無効ですか?class null_extension { String ToString( Object this arg ) { return ToString(arg); } }
ctrl-alt-delor 2012年

回答:


163

最初のものが機能する理由:

MSDNから:

文字列連結演算では、C#コンパイラはnull文字列を空の文字列と同様に扱いますが、元のnull文字列の値は変換しません。

詳細情報+バイナリ演算子

二項+演算子は、一方または両方のオペランドが文字列型である場合、文字列連結を実行します。

文字列連結のオペランドがnullの場合、空の文字列が置換されます。それ以外の場合、文字列以外の引数はToString、型オブジェクトから継承された仮想メソッドを呼び出すことにより、文字列表現に変換されます。

がをToString返すnull場合、空の文字列が置換されます。

秒単位のエラーの理由は次のとおりです。

null(C#参照) -nullキーワードは、オブジェクトを参照しないnull参照を表すリテラルです。nullは、参照型変数のデフォルト値です。


1
これでうまくいくでしょうか?var wtf = ((String)null).ToString();私が最近Javaで作業していて、nullのキャストが可能な場合、C#で作業してからしばらく経ちました。
Jochem、2012年

14
@Jochem:nullオブジェクトでメソッドを呼び出そうとしているので、私はそうではないと思います。それをnull.ToString()vsと考えてくださいToString(null)
2012年

@Svishはい、もう一度考えます。これはnullオブジェクトです。そのため、あなたの言うとおり、機能しません。Javaでもそうではありません。nullポインタ例外です。気にしないで。Tnx for your reply![編集:Javaでテストしました:NullPointerException。キャストではコンパイルされるが、キャストなしではコンパイルされないという違いがある。
Jochem、2012年

通常、意図的にnullキーワードを連結またはToStringする理由はありません。空の文字列が必要な場合は、string.Emptyを使用します。文字列変数がnullかどうかを確認する必要がある場合は、(myString == null)またはstring.IsNullOrEmpty(myString)を使用できます。または、null文字列変数をstring.Emptyに変換するには、myNewString =(myString == null ?? string.Empty)を使用します
csauve

@Jochemをキャストするとコンパイルされますが、実際には実行時にNullReferenceExceptionがスローされます
jeroenh

108

+C#の演算子は内部的にに変換されるためString.Concat、これは静的メソッドです。そして、このメソッドはたまたまnull空の文字列のように扱われます。String.ConcatReflector のソースを見ると、次のことがわかります。

// while looping through the parameters
strArray[i] = (str == null) ? Empty : str;
// then concatenate that string array

(MSDNも言及しています:http : //msdn.microsoft.com/en-us/library/k9c94ey1.aspx

一方、ToString()は呼び出すことができないインスタンスメソッドですnull(どのタイプを使用する必要がありnullますか?)。


2
Stringクラスには+演算子はありませんが、コンパイラはString.Concatに変換するのでしょうか?
superlogical

@superlogicalはい、それはコンパイラが行うことです。したがって、実際には+、C#の文字列の演算子はの構文糖衣ですString.Concat
Botz3000 2012年

それに追加:コンパイラーは、最も意味のあるConcatのオーバーロードを自動的に選択します。使用可能なオーバーロードは、1、2、または3つのオブジェクトパラメータ、4つのオブジェクトパラメータ+ __arglist、およびparamsオブジェクト配列バージョンです。
Wormbo、2012年

そこではどの文字列配列が変更されていますか?Concat常にnull以外の文字列の配列を与えられた、または他の何かが起こっても、非ヌル文字列の新しい配列を作成しますか?
スーパーキャット2015

@supercat変更された配列は新しい配列で、プライベートヘルパーメソッドに渡されます。リフレクターを使用して自分でコードを確認できます。
Botz3000 2015

29

最初のサンプルはに翻訳されます。

var bob = String.Concat("abc123", null, null, null, "abs123");

このConcatメソッドは入力をチェックし、nullを空の文字列として変換します

第二のサンプルをに翻訳されます。

var wtf = ((object)null).ToString();

したがって、null参照例外がここで生成されます


実際には、a AccessViolationExceptionがスローされます:)
leppie '30年

私はたった今:) ((object)null).ToString()=> AccessViolation ((object)1 as string).ToString()=>NullReference
leppie

1
NullReferenceExceptionが発生しました。DN 4.0
Viacheslav Smityukh 2012年

面白い。私が((object)null).ToString()中に入れたとき、try/catch私も得るNullReferenceException
レッピー

3
@Ieppieそれ以外はAccessViolationもスローしません(なぜそうなりますか?)-NullReferenceExceptionがここにあります。
jeroenh

11

あなたのコードの最初の部分は、ちょうどそれのように扱われString.Concat

これは、文字列を追加するときにC#コンパイラが呼び出すものです。" abc" + nullはに翻訳されますString.Concat("abc", null)

内部的には、このメソッドはに置き換えnullられString.Emptyます。したがって、コードの最初の部分で例外がスローされないのはそのためです。まるで

var bob = "abc" + string.Empty + string.Empty + string.Empty + "123";  //abc123

また、「null」はオブジェクトではないため、コードの2番目の部分で例外がスローされます。nullキーワードは、オブジェクトを参照しないnull参照を表すリテラルです。nullは、参照型変数のデフォルト値です。

また、「ToString()」は、オブジェクトのインスタンスから呼び出すことはできますが、リテラルからは呼び出せないメソッドです。


3
String.Emptyと等しくないnull。のようないくつかのメソッドでは同じように扱われString.Concatます。たとえば、文字列変数をに設定している場合null、C#はString.Empty、そのメソッドを呼び出そうとしても、置換されません。
Botz3000 2012年

3
ほとんど。nullString.EmptyC#のようには扱われませんが、C#の場合と同様に扱われString.Concatます。これは、文字列を追加するときにC#コンパイラが呼び出すものです。"abc" + nullに翻訳されString.Concat("abc", null)、内部的にそのメソッドはに置き換えnullられString.Emptyます。2番目の部分は完全に正しいです。
Botz3000 2012年

9

.netに先行するCOMフレームワークでは、文字列を受け取ったルーチンは、それを使用してそれを解放する必要がありました。空の文字列がルーチンとの間でやり取りされることは非常に一般的であり、ヌルポインターを「解放」する試みは正当な何もしない操作として定義されていたため、Microsoftは空の文字列を表すヌル文字列ポインターを持つことを決定しました。

COMとの互換性を確保するために、.netの多くのルーチンはnullオブジェクトを正当な表現として空の文字列として解釈します。いくつかのわずかな変更.netとその言語(特にインスタンスメンバーが「仮想として呼び出さない」を示すことを可能にする)により、Microsoft nullは宣言型Stringのオブジェクトを空の文字列のように動作させることができました。マイクロソフトがそれを行っていた場合、Nullable<T>Nullable<String>とにかくIMHOがやるべきことを許可するために)多少異なる方法で作業を行うか、またはNullableStringとほとんど互換性Stringがあるが、null有効な空の文字列として。

そのままでは、nullaが正当な空の文字列と見なされるコンテキストとそうでないコンテキストがあります。ひどく役立つ状況ではありませんが、プログラマーが知っておくべき状況です。一般に、フォームの式はis stringValue.someMemberif stringValueで失敗nullしますが、パラメータとして文字列を受け入れるほとんどのフレームワークメソッドおよび演算子nullは空の文字列と見なされます。


5

'+'中置演算子です。他の演算子と同様に、実際にはメソッドを呼び出しています。あなたは非中置版を想像することができます"wow".Plus(null) == "wow"

実装者はこのようなことを決定しました...

class String
{
  ...
  String Plus(ending)
  {
     if(ending == null) return this;
     ...
  }
} 

だからあなたの例は

var bob = "abc".Plus(null).Plus(null).Plus(null).Plus("123");  // abc123

それは同じです

var bob = "abc".Plus("123");  // abc123

nullが文字列になることはありません。ですからnull.ToString()、違いはありません null.VoteMyAnswer()。;)


本当に、もっと似ていvar bob = Plus(Plus(Plus(Plus("abc",null),null),null),"123");ます。演算子のオーバーロードは、彼らの心の静的メソッドです:msdn.microsoft.com/en-us/library/s53ehcz3(v=VS.71).aspxは彼らがいなかった、var bob = null + "abc";または特にstring bob = null + null;有効ではありません。
Ben Mosher

そうです、私がそのように説明すると、私は混乱しすぎると感じました。
Nigel Thorne


3

誰かがこのディスカッションスレッドで、何もないところから文字列を作成することはできないと述べました。 (私が思うに、これは素晴らしいフレーズです)。しかし、はい-次の例に示すように、:-) できます。

var x = null + (string)null;     
var wtf = x.ToString();

正常に動作し、例外をまったくスローしません。唯一の違いは、nullの1つを文字列にキャストする必要があることです。(文字列)キャストを削除した場合、例は引き続きコンパイルされますが、実行時例外がスローされます。 「<null>」と「<null>」を入力します。

注意上記のコード例では、xの値は期待どおりにnullではなく、オペランドの1つを文字列にキャストした後は実際には空の文字列です。


もう1つの興味深い事実は、C#/ .NETではnull異なるデータ型を考慮すると処理方法が常に同じになるとは限らないことです。例えば:

int? x = 1;  //  string x = "1";
x = x + null + null;
Console.WriteLine((x==null) ? "<null>" : x.ToString());

コードスニペットの1行目について:がvalueを含むxnull許容整数変数(つまりint?)の場合1、結果が<null>返されます。それが値を持つ文字列(コメントに示されている)の場合"1"は、ではなく"1"戻ります<null>

:また興味深い:var x = 1;最初の行に使用している場合は、ランタイムエラーが発生します。どうして?これは、代入によって変数xがdatatypeに変わるためですint。これはnullにできません。コンパイラはint?ここを想定していないため、nullが追加されている2行目で失敗します。


2

null文字列への追加は単に無視されます。null(2番目の例では)はオブジェクトのインスタンスではないため、ToString()メソッドもありません。それは単なるリテラルです。


1

差がないため、string.Emptyそしてnullあなたは、文字列をCONCATが。nullも渡すことができますstring.Format。ただし、でメソッドを呼び出そうとすると、null常にがNullReferenceException発生し、コンパイラエラーが発生します。
何らかの理由で本当にそれを実行したい場合は、チェックしてnullからを返す拡張メソッドを作成できますstring.Empty。しかし、そのような拡張機能は絶対に必要な場合にのみ使用する必要があります(私の意見では)。


0

一般的に:仕様に応じてnullをパラメーターとして受け入れることは有効な場合と無効な場合がありますが、nullでメソッドを呼び出すことは常に無効です。


これが、文字列の場合に+演算子のオペランドがnullになる理由です。これは、プログラマーの生活を楽にするVBのこと(ごめんなさい)、またはプログラマーがnullを処理できない場合を想定しています。この仕様には完全に同意しません。'unknown' + 'anything'はまだ 'unknown'でなければなりません...

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