string.Equals ()와 == 연산자가 실제로 동일합니까? [복제]
이 질문에는 이미 답변이 있습니다.
- ==와 Equals ()의 C # 차이 17 답변
그들은 정말 같은가요? 오늘 나는이 문제에 부딪쳤다. 직접 실행 창에서 나온 덤프는 다음과 같습니다.
?s
"Category"
?tvi.Header
"Category"
?s == tvi.Header
false
?s.Equals(tvi.Header)
true
?s == tvi.Header.ToString()
true
그래서, 모두 s
와 tvi.Header
"분류"를 포함하지만, ==
거짓을 반환하고 Equals()
true를 반환합니다.
s
string으로 정의되며 tvi.Header
실제로 WPF TreeViewItem.Header
입니다. 그렇다면 왜 다른 결과를 반환합니까? 나는 항상 C #에서 상호 교환 가능하다고 생각했습니다.
아무도 이것이 왜 설명 할 수 있습니까?
두 가지 차이점 :
Equals
는 다형성 (즉, 재정의 될 수 있고 사용 된 구현은 대상 객체의 실행 시간 유형에 따라 다름)이지만 사용 된 구현은 객체==
의 컴파일 시간 유형에 따라 결정됩니다.// Avoid getting confused by interning object x = new StringBuilder("hello").ToString(); object y = new StringBuilder("hello").ToString(); if (x.Equals(y)) // Yes // The compiler doesn't know to call ==(string, string) so it generates // a reference comparision instead if (x == y) // No string xs = (string) x; string ys = (string) y; // Now *this* will call ==(string, string), comparing values appropriately if (xs == ys) // Yes
Equals
null로 호출하면 뱅킹됩니다. ==string x = null; string y = null; if (x.Equals(y)) // Bang if (x == y) // Yes
다음을 사용하여 후자가 문제가되는 것을 피할 수 있습니다 object.Equals
.
if (object.Equals(x, y)) // Fine even if x or y is null
질문에 나타나는 명백한 모순은 어떤 경우에는 Equals
함수가 string
객체에서 호출되고 다른 경우에는 ==
연산자가 System.Object
유형에서 호출되기 때문에 발생 합니다. 서로 다르게 평등 string
을 object
구현하십시오 (각각 값 대 기준).
이 사실 이외에도 모든 유형은 정의 할 수 ==
있고 Equals
다르게 정의 할 수 있으므로 일반적으로 상호 교환 할 수 없습니다.
다음은 double
(Joseph Albahari의 메모에서 C # 언어 사양의 §7.9.2까지) 를 사용하는 예입니다 .
double x = double.NaN;
Console.WriteLine (x == x); // False
Console.WriteLine (x != x); // True
Console.WriteLine (x.Equals(x)); // True
그는이 double.Equals(double)
방법이 목록과 사전에서 올바르게 작동하도록 설계 되었다고 말합니다 . 반면에 ==
연산자는 부동 소수점 유형에 대한 IEEE 754 표준을 따르도록 설계되었습니다.
스트링 동등성을 결정하는 특정한 경우에, 산업 선호는 대부분 ==
또는 string.Equals(string)
대부분 을 사용하지 않는 것이다. 이 방법은 두 문자열이 문자마다 동일한 문자인지 여부를 판별하며 이는 올바른 동작이 아닙니다. string.Equals(string, StringComparison)
특정 비교 유형을 지정할 수있는을 사용하는 것이 좋습니다 . 올바른 비교를 사용하면 많은 잠재적 (진단하기 어려운) 버그를 피할 수 있습니다.
예를 들면 다음과 같습니다.
string one = "Caf\u00e9"; // U+00E9 LATIN SMALL LETTER E WITH ACUTE
string two = "Cafe\u0301"; // U+0301 COMBINING ACUTE ACCENT
Console.WriteLine(one == two); // False
Console.WriteLine(one.Equals(two)); // False
Console.WriteLine(one.Equals(two, StringComparison.InvariantCulture)); // True
이 예제에서 두 문자열은 모두 동일하게 보이므로 ( "카페") 순진한 (균등 한) 평등을 사용하는 경우 디버깅하기가 매우 어려울 수 있습니다.
C #은이 "동일"개념을 가지고 Equals
와 ReferenceEquals
. 발생하는 대부분의 클래스에서 ==
연산자는 하나 또는 다른 클래스 (또는 둘 다)를 사용하며 일반적으로 ReferenceEquals
참조 유형을 처리 할 때만 테스트합니다 (그러나 string
클래스는 C #이 값 평등을 테스트하는 방법을 이미 알고있는 인스턴스입니다).
Equals
값을 비교합니다. (int
메모리의 동일한 지점에 두 개의 개별 변수가 존재하지 않더라도 여전히 동일한 값을 포함 할 수 있습니다.)ReferenceEquals
참조를 비교하고 피연산자가 메모리의 동일한 객체를 가리키는 지 여부를 반환합니다.
예제 코드 :
var s1 = new StringBuilder("str");
var s2 = new StringBuilder("str");
StringBuilder sNull = null;
s1.Equals(s2); // True
object.ReferenceEquals(s1, s2); // False
s1 == s2 // True - it calls Equals within operator overload
s1 == sNull // False
object.ReferenceEquals(s1, sNull); // False
s1.Equals(sNull); // Nono! Explode (Exception)
의 Header
속성은 TreeViewItem
유형이 정적으로 형식화됩니다 object
.
따라서 ==
수율 false
. 다음과 같은 간단한 스 니펫으로이를 재현 할 수 있습니다.
object s1 = "Hallo";
// don't use a string literal to avoid interning
string s2 = new string(new char[] { 'H', 'a', 'l', 'l', 'o' });
bool equals = s1 == s2; // equals is false
equals = string.Equals(s1, s2); // equals is true
Jon Skeet의 답변 외에도 , ==
당신이 사용할 때 대부분의 시간 이 실제로 true
같은 값을 가진 다른 문자열 인스턴스에 대한 답변 을 얻는 이유를 설명하고 싶습니다 .
string a = "Hell";
string b = "Hello";
a = a + "o";
Console.WriteLine(a == b);
당신이 볼 수로서, a
그리고 b
다른 문자열 인스턴스해야하지만 문자열은 불변이기 때문에, 소위 런타임 사용 인턴 문자열이 모두 들어 가지 a
와 b
메모리에 동일한 문자열을 참조합니다. ==
객체 의 연산자는 참조를 확인 a
하며 b
, 동일한 인스턴스를 참조 하고 참조하므로 결과는 true
입니다. 둘 중 하나를 변경하면 새 문자열 인스턴스가 만들어 지므로 문자열 삽입이 가능합니다.
그건 그렇고, Jon Skeet의 대답은 완전하지 않습니다. 사실 x == y
입니다 false
하지만 그가 개체를 비교하고, 객체 참조에 의해 비교 때문이다. 쓰면 다시 (string)x == (string)y
돌아 true
옵니다. 따라서 문자열에는 == 연산자가 오버로드되어 String.Equals
아래에 호출 됩니다.
여기에 설명적인 답변이 많이 있으므로 이미 말한 내용을 반복하지 않겠습니다. 내가 추가하고 싶은 것은 내가 생각할 수있는 모든 순열을 보여주는 다음 코드입니다. 조합 수로 인해 코드가 매우 깁니다. MSTest에 자유롭게 놓고 출력을 확인하십시오 (출력은 하단에 포함됨).
이 증거는 Jon Skeet의 답변을 뒷받침합니다.
암호:
[TestMethod]
public void StringEqualsMethodVsOperator()
{
string s1 = new StringBuilder("string").ToString();
string s2 = new StringBuilder("string").ToString();
Debug.WriteLine("string a = \"string\";");
Debug.WriteLine("string b = \"string\";");
TryAllStringComparisons(s1, s2);
s1 = null;
s2 = null;
Debug.WriteLine(string.Join(string.Empty, Enumerable.Repeat("-", 20)));
Debug.WriteLine(string.Empty);
Debug.WriteLine("string a = null;");
Debug.WriteLine("string b = null;");
TryAllStringComparisons(s1, s2);
}
private void TryAllStringComparisons(string s1, string s2)
{
Debug.WriteLine(string.Empty);
Debug.WriteLine("-- string.Equals --");
Debug.WriteLine(string.Empty);
Try((a, b) => string.Equals(a, b), s1, s2);
Try((a, b) => string.Equals((object)a, b), s1, s2);
Try((a, b) => string.Equals(a, (object)b), s1, s2);
Try((a, b) => string.Equals((object)a, (object)b), s1, s2);
Debug.WriteLine(string.Empty);
Debug.WriteLine("-- object.Equals --");
Debug.WriteLine(string.Empty);
Try((a, b) => object.Equals(a, b), s1, s2);
Try((a, b) => object.Equals((object)a, b), s1, s2);
Try((a, b) => object.Equals(a, (object)b), s1, s2);
Try((a, b) => object.Equals((object)a, (object)b), s1, s2);
Debug.WriteLine(string.Empty);
Debug.WriteLine("-- a.Equals(b) --");
Debug.WriteLine(string.Empty);
Try((a, b) => a.Equals(b), s1, s2);
Try((a, b) => a.Equals((object)b), s1, s2);
Try((a, b) => ((object)a).Equals(b), s1, s2);
Try((a, b) => ((object)a).Equals((object)b), s1, s2);
Debug.WriteLine(string.Empty);
Debug.WriteLine("-- a == b --");
Debug.WriteLine(string.Empty);
Try((a, b) => a == b, s1, s2);
#pragma warning disable 252
Try((a, b) => (object)a == b, s1, s2);
#pragma warning restore 252
#pragma warning disable 253
Try((a, b) => a == (object)b, s1, s2);
#pragma warning restore 253
Try((a, b) => (object)a == (object)b, s1, s2);
}
public void Try<T1, T2, T3>(Expression<Func<T1, T2, T3>> tryFunc, T1 in1, T2 in2)
{
T3 out1;
Try(tryFunc, e => { }, in1, in2, out out1);
}
public bool Try<T1, T2, T3>(Expression<Func<T1, T2, T3>> tryFunc, Action<Exception> catchFunc, T1 in1, T2 in2, out T3 out1)
{
bool success = true;
out1 = default(T3);
try
{
out1 = tryFunc.Compile()(in1, in2);
Debug.WriteLine("{0}: {1}", tryFunc.Body.ToString(), out1);
}
catch (Exception ex)
{
Debug.WriteLine("{0}: {1} - {2}", tryFunc.Body.ToString(), ex.GetType().ToString(), ex.Message);
success = false;
catchFunc(ex);
}
return success;
}
산출:
string a = "string";
string b = "string";
-- string.Equals --
Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True
-- object.Equals --
Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True
-- a.Equals(b) --
a.Equals(b): True
a.Equals(Convert(b)): True
Convert(a).Equals(b): True
Convert(a).Equals(Convert(b)): True
-- a == b --
(a == b): True
(Convert(a) == b): False
(a == Convert(b)): False
(Convert(a) == Convert(b)): False
--------------------
string a = null;
string b = null;
-- string.Equals --
Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True
-- object.Equals --
Equals(a, b): True
Equals(Convert(a), b): True
Equals(a, Convert(b)): True
Equals(Convert(a), Convert(b)): True
-- a.Equals(b) --
a.Equals(b): System.NullReferenceException - Object reference not set to an instance of an object.
a.Equals(Convert(b)): System.NullReferenceException - Object reference not set to an instance of an object.
Convert(a).Equals(b): System.NullReferenceException - Object reference not set to an instance of an object.
Convert(a).Equals(Convert(b)): System.NullReferenceException - Object reference not set to an instance of an object.
-- a == b --
(a == b): True
(Convert(a) == b): True
(a == Convert(b)): True
(Convert(a) == Convert(b)): True
tvi.header
이 아닌 것이 분명합니다 String
. 가 ==
과부하되는 연산자 String
컴파일러 조작자의 양측이라는 것을 아는 경우에만 작동되는 것을 의미하는 클래스 String
.
객체는 고유 한 OBJECT_ID로 정의됩니다. A와 B가 객체이고 A == B가 true 인 경우, 이들은 매우 동일한 객체이며 동일한 데이터와 방법을 갖지만,이 또한 사실입니다.
A.OBJECT_ID == B.OBJECT_ID
A.Equals (B)가 true이면 두 객체가 같은 상태에 있다는 것을 의미하지만 A가 B와 매우 동일하다는 의미는 아닙니다.
문자열은 객체입니다.
== 및 Equals 연산자는 반사적이고, 비대칭적이고, 트랜 지 티브하므로 등가 관계 (관계형 대수 용어를 사용하기 위해)입니다.
의미 : A, B 및 C가 객체 인 경우 :
(1) A == A는 항상 참입니다. A. 같음 (A)은 항상 참 (반사율)
(2) A == B이면 B == A; A.Equals (B)이면 B.Equals (A) (simetry)
(3) A == B이고 B == C이면 A == C; A.Equals (B) 및 B.Equals (C) 인 경우 A.Equals (C) (고유도)
또한 이것이 사실임을 알 수 있습니다.
(A == B) => (A.Equals (B))이지만 그 반대는 아닙니다.
A B =>
0 0 1
0 1 1
1 0 0
1 1 1
실생활의 예 : 같은 유형의 햄버거 두 개는 같은 속성을 갖습니다. 그것들은 Hamburger 클래스의 객체이며, 속성은 정확히 같지만 다른 엔티티입니다. 이 두 햄버거를 사서 하나를 먹으면 다른 하나는 먹지 않습니다. 따라서 Equals와 ==의 차이점은 hamburger1과 hamburger2입니다. 그들은 정확히 같은 상태 (같은 무게, 같은 온도, 같은 맛)에 있으므로 hamburger1.Equals (hamburger2)는 사실입니다. 그러나 hamburger1 == hamburger2는 false입니다. hamburger1의 상태가 변경되면 hamburger2의 상태가 반드시 변경되는 것은 아니며 그 반대도 마찬가지입니다.
당신과 친구가 동시에 당신과 그의 햄버거를 얻는다면, 당신은 햄버거를 두 부분으로 나눠야합니다. : friend.eatHamburger (), 햄버거도 먹습니다.
나는 Equals와 ==에 대해 다른 뉘앙스를 쓸 수 있지만 배가 고파지고 있으므로 가야합니다.
감사합니다, 라 호스 아파 드
참고 URL : https://stackoverflow.com/questions/3678792/are-string-equals-and-operator-really-same
'Programing' 카테고리의 다른 글
git pull을 취소하는 방법? (0) | 2020.04.21 |
---|---|
jQuery Datepicker onchange 이벤트 문제 (0) | 2020.04.21 |
반영-속성의 속성 이름 및 값 가져 오기 (0) | 2020.04.21 |
명령 줄을 통해 unittest.TestCase에서 단일 테스트 실행 (0) | 2020.04.21 |
CSS : before / : after pseudo-elements에서 이미지의 높이를 변경할 수 있습니까? (0) | 2020.04.21 |