There are plenty of descriptive answers here so I'm not going to repeat what has already been said. What I would like to add is the following code demonstrating all the permutations I can think of. The code is quite long due to the number of combinations. Feel free to drop it into MSTest and see the output for yourself (the output is included at the bottom).
This evidence supports Jon Skeet's answer.
Code:
[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; }
Output:
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
string.Equalsoverloads that include aStringComparisonparameter for most string comparisons. Use the InvarientCulture versions for coded strings (XML attributes for example) or CurrentCulture for user-entered strings. This will take care of a lot of details that == ignores, such as Unicode character normalization forms, etc., and it makes case sensitivity explicit.