私は最近大規模なコードベースを紹介され、すべての文字列比較が==
の代わりにString.Equals()
を使って行われることに気付きました。
その理由は何ですか、あなたは思いますか?
開発者ベースの大部分は、文字列を比較するための==
の使用が間違っていて機能しないというJavaの背景に由来する可能性があります。
C#では、文字列として型付けされている限り、(実用的な)違いはありません(文字列について)。
それらがobject
またはT
として型付けされている場合は、ここで他の回答を参照してください。ここで他の回答を参照してください。
string.Equals
と==
の間には実用的な違いがあります
bool result = false;
object obj = "String";
string str2 = "String";
string str3 = typeof(string).Name;
string str4 = "String";
object obj2 = str3;
// Comparision between object obj and string str2 -- Com 1
result = string.Equals(obj, str2);// true
result = String.ReferenceEquals(obj, str2); // true
result = (obj == str2);// true
// Comparision between object obj and string str3 -- Com 2
result = string.Equals(obj, str3);// true
result = String.ReferenceEquals(obj, str3); // false
result = (obj == str3);// false
// Comparision between object obj and string str4 -- Com 3
result = string.Equals(obj, str4);// true
result = String.ReferenceEquals(obj, str4); // true
result = (obj == str4);// true
// Comparision between string str2 and string str3 -- Com 4
result = string.Equals(str2, str3);// true
result = String.ReferenceEquals(str2, str3); // false
result = (str2 == str3);// true
// Comparision between string str2 and string str4 -- Com 5
result = string.Equals(str2, str4);// true
result = String.ReferenceEquals(str2, str4); // true
result = (str2 == str4);// true
// Comparision between string str3 and string str4 -- Com 6
result = string.Equals(str3, str4);// true
result = String.ReferenceEquals(str3, str4); // false
result = (str3 == str4);// true
// Comparision between object obj and object obj2 -- Com 7
result = String.Equals(obj, obj2);// true
result = String.ReferenceEquals(obj, obj2); // false
result = (obj == obj2);// false
ウォッチを追加する
obj "String" {1#} object {string}
str2 "String" {1#} string
str3 "String" {5#} string
str4 "String" {1#} string
obj2 "String" {5#} object {string}
それでは{1#}
と{5#}
を見てください。
obj
、str2
、str4
、およびobj2
参照は同じです。
obj
とobj2
はobject type
で、その他はstring type
です。
結論:
object
とstring
を比較して、参照等価検査を実行します。object
とstring
を比較して、参照等価検査を実行します。object
とstring
を比較して、参照等価検査を実行します。string
とstring
を比較して、文字列値のチェックを行います。string
とstring
を比較して、文字列値のチェックを行います。string
とstring
を比較して、文字列値のチェックを行います。object
とobject
を比較します。異なる参照なので結果はfalse==とString.Equalsメソッドの間に微妙ですが非常に重要な違いがあります。
class Program
{
static void Main(string[] args)
{
CheckEquality("a", "a");
Console.WriteLine("----------");
CheckEquality("a", "ba".Substring(1));
}
static void CheckEquality<T>(T value1, T value2) where T : class
{
Console.WriteLine("value1: {0}", value1);
Console.WriteLine("value2: {0}", value2);
Console.WriteLine("value1 == value2: {0}", value1 == value2);
Console.WriteLine("value1.Equals(value2): {0}", value1.Equals(value2));
if (typeof(T).IsEquivalentTo(typeof(string)))
{
string string1 = (string)(object)value1;
string string2 = (string)(object)value2;
Console.WriteLine("string1 == string2: {0}", string1 == string2);
}
}
}
この出力を生成します:
value1: a value2: a value1 == value2: True value1.Equals(value2): True string1 == string2: True ---------- value1: a value2: a value1 == value2: False value1.Equals(value2): True string1 == string2: True
==演算子が2つの明らかに等しい文字列にfalseを返していることがわかります。どうして?汎用メソッドで使用されている==演算子はSystem.Objectで定義されているop_equalメソッド(コンパイル時にTが保証されている唯一の保証)であると解決されるため、値等価ではなく参照等価です。
System.Stringとして明示的に型指定された2つの値がある場合、==はSystem.Object.op_equalではなくSystem.String.op_equalに解決されるため、==の値は等価になります。
安全のために、私はほとんど常にString.Equalsを使用しています。その代わりに、必要な値等価性セマンティクスを常に取得しています。
値の1つがnullの場合にNullReferenceExceptionsを避けるために、私は常にstatic String.Equalsメソッドを使用します。
bool true = String.Equals("a", "ba".Substring(1));
String.Equals
は、大文字小文字の区別と文化を意識した比較を処理するためのオーバーロードを提供します。あなたのコードがこれらを利用しないのであれば、開発者は単にJavaに慣れているかもしれません、そこで(Matthewが言っているように)、内容の比較をするために.Equalsメソッドを使わなければなりません。
どちらの方法も機能的に同じします - 値を比較します。
MSDNに書かれているように:
String.Equals
メソッドについて - このインスタンスと指定された別のStringオブジェクトが同じ値を持つかどうかを判断します。 ( http://msdn.Microsoft.com/en-us/library/858x0yyx.aspx )==
について - stringは参照型ですが、等価演算子(==
と!=
)は参照ではなく文字列オブジェクトの値を比較するために定義されています。これにより、文字列の等価性のテストがより直感的になります。 ( http://msdn.Microsoft.com/en-en/library/362314fe.aspx )しかし、文字列インスタンスの1つがnullの場合、これらのメソッドは異なる動作をします。
string x = null;
string y = "qq";
if (x == y) // returns false
MessageBox.Show("true");
else
MessageBox.Show("false");
if (x.Equals(y)) // returns System.NullReferenceException: Object reference not set to an instance of an object. - because x is null !!!
MessageBox.Show("true");
else
MessageBox.Show("false");
Jon Skeetからのいくつかの引用とともに、 この記事 に興味深い記事があります。使い方はほぼ同じです。
Jon Skeet氏は、インスタンスEqualsのパフォーマンスは「文字列が短い場合はわずかに優れています。文字列の長さが増すにつれて、その差はまったく意味がなくなります」と述べています。
さらに違いがあることを付け加えたいと思います。それはAndrewが投稿したものと関係があります。
それはまた私達のソフトウェアのバグを見つけることが非常に迷惑なことに関連しています。次の簡単な例を参照してください(私もnullチェックを省略しました)。
public const int SPECIAL_NUMBER = 213;
public bool IsSpecialNumberEntered(string numberTextBoxTextValue)
{
return numberTextBoxTextValue.Equals(SPECIAL_NUMBER)
}
これはコンパイルされ、常にfalse
を返します。以下はコンパイルエラーになりますが:
public const int SPECIAL_NUMBER = 213;
public bool IsSpecialNumberEntered(string numberTextBoxTextValue)
{
return (numberTextBoxTextValue == SPECIAL_NUMBER);
}
誰かがEquals
を使って異なる型のenumを比較するという同様の問題を解決しなければなりませんでした。それがバグの原因であることに気づく前に、このたくさんの時間を読み返すつもりです。特にSPECIAL_NUMBER
の定義が問題領域の近くにない場合。
これが私が本当に必要でない状況でEqualsの使用に本当に反対である理由です。あなたは少し型安全性を失います。
このページを読み、実際には意味のある違いはないと結論を下したので、バグを解決しようとしている壁に頭をぶつけているところです。 ==から外れて等しい。
Object ==等価は失敗しますが、.Equalsは成功します。これは意味がありますか?
string a = "x";
string b = new String(new []{'x'});
Console.WriteLine("x == x " + (a == b));//True
Console.WriteLine("object x == x " + ((object)a == (object)b));//False
Console.WriteLine("x equals x " + (a.Equals(b)));//True
Console.WriteLine("object x equals x " + (((object)a).Equals((object)b)));//True