【问题标题】:Why would you use String.Equals over ==? [duplicate]为什么要使用 String.Equals 而不是 ==? [复制]
【发布时间】:2009-11-02 01:55:25
【问题描述】:

我最近被介绍到一个大型代码库,并注意到所有字符串比较都是使用 String.Equals() 而不是 == 完成的

你认为这是什么原因?

【问题讨论】:

  • Re : Duplicateclosure - 注意这个问题是专门针对字符串的,而在引用的副本中接受的答案是指对象的一般情况。这在 .Net 和 Java 中有很大不同,因为 == 可用于比较 C# 中的字符串内容。
  • Equals 可能是首选,因为 null-propagation 运算符 的出现,因为您也可以在两个字符串 null 的情况下返回 false,在非常简洁的方式;像这样:if(firstString?.Equals(secondString) ?? false)

标签: c# string equals


【解决方案1】:

很可能大部分开发人员来自 Java 背景,使用 == 比较字符串是错误的并且不起作用。

在 C# 中,只要将它们键入为字符串,就没有(实际的)区别(对于字符串)。

如果它们被键入为objectT,那么请在此处查看其他有关泛型方法或运算符重载的答案,因为您肯定想使用 Equals 方法。

【讨论】:

  • 没有返回的值。 .Equals 确实提供了更多选项,但就您得到的结果而言,str1.Equals(str2)str1 == str2 相同。我确实相信他们使用不同的方法来确定平等,正如 Jon Skeets 引用 Blaenk 所指出的那样,但他们给出了相同的值。
  • @Yuriy 也许您可以详细说明或提供有关差异的链接?
  • 差别很大。如果其中一个字符串为 null,则 .Equals 将引发异常。
  • @Mas:.Equals 抛出的唯一异常是空引用异常,如果您尝试在空对象上调用它,即。 string str1 = null; str1.Equals()。这不是Equals() 的问题,任何功能都会发生这种情况,并且应该是不言而喻的。给定相同的输入,两次比较的实际功能始终完全相同。
  • 上面的链接失效了,但是这个链接可以了:dotnetperls.com/string-equals
【解决方案2】:

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#}

objstr2str4obj2 引用相同。

objobj2object type,其他的是 string type

Conclusion

  1. com1: result = (obj == str2);// true
    • 比较 objectstring 以执行引用相等检查
    • obj和str2指向同一个引用所以结果为真
  2. com2: result = (obj == str3);// false
    • 比较 objectstring 以执行引用相等检查
    • obj 和 str3 指向不同的引用,所以结果为假
  3. com3: result = (obj == str4);// true
    • 比较 objectstring 以执行引用相等检查
    • obj和str4指向同一个引用所以结果为真
  4. com4: result = (str2 == str3);// true
    • 比较stringstring所以执行字符串值检查
    • str2 和 str3 都是“字符串”,所以结果为真
  5. com5: result = (str2 == str4);// true
    • 比较stringstring 所以执行字符串值检查
    • str2 和 str4 都是“字符串”,所以结果为真
  6. com6: result = (str3 == str4);// true
    • 比较stringstring 所以执行字符串值检查
    • str3 和 str4 都是“字符串”,所以结果为真
  7. com7: result = (obj == obj2);// false - 比较 objectobject 执行引用相等检查 - obj 和 obj2 指向不同的引用,所以结果为假

【讨论】:

  • 对上述所有情况的简单解释:string.Equals(object, object) is object.Equals(object, object) 在第一个参数上调用Equals,不像== 运算符,除非重载,致电ReferenceEquals
  • 我认为"String"typeof(string).Name是不同引用的原因是前者作为编译时常量是interned,而后者作为运行时值,不是。
  • 为什么Equals 使用camelCase stringReferenceEquals 使用CapCase String
  • @AlexMcMillan,我可以使用string 而不是String,因为string 只是System.String 的别名
  • 回复:“ComX”。回到过去糟糕的 DOS 时代,我收到了一些数据,其中有人将文件命名为“com3”(在这种情况下,“Com”表示“community”,而不是“comparison”)。当然,在尝试访问文件时,操作系统认为它是一个 com 端口,结果很有趣。 30 年后,但我不会将“任何东西”命名为“com-whatever”,除非它真的是一个 com 端口。
【解决方案3】:

== 和 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

您可以看到 == 运算符将 false 返回到两个明显相等的字符串。为什么?因为在泛型方法中使用的 == 运算符被解析为由 System.Object 定义的 op_equal 方法(该方法在编译时具有 T 的唯一保证),这意味着它是引用相等而不是值相等。

当您有两个明确键入为 System.String 的值时,== 具有值相等语义,因为编译器将 == 解析为 System.String.op_equal 而不是 System.Object.op_equal。

所以为了安全起见,我几乎总是使用 String.Equals 来代替我总是得到我想要的值相等语义。

如果其中一个值为 null,为了避免 NullReferenceExceptions,我总是使用 static String.Equals 方法:

bool true = String.Equals("a", "ba".Substring(1));

【讨论】:

  • 返回 Object 类型的代码会出现相关问题。如果var myItem=GetItem() 从数据库中读取Object,则表达式"Hi".Equals(myItem)myItem.Equals("Hi")Object.Equals("Hi", myItem) 都将返回True,如果返回的对象是带有两个字符“Hi”的string,但myItem == "Hi""Hi" == myItem 将测试引用相等性。 VB.NET 的“Option Strict On”方言在这方面更好。它的 "=" 操作符测试要么测试值相等,要么不会编译;对于引用相等性检查,使用“Is”运算符。
  • @AndrewArnott 如果我在csharppad.com 上执行"a" == "ab".Substring(0, 1),我会返回true。如果我使用 String 对象,则相同。解释?
  • 因为在您的示例中,编译器知道调用 System.String 运算符的 System.String 重载。您必须小心的情况是编译器知道它是什么类型,例如比较表达式的类型为TSystem.Object
  • @AndrewArnott 您能否详细说明为什么.Substring() 会导致不同的行为?由于它返回 string,而不是包裹在 object 中的 string,为什么它的行为与第一次检查不同?对我来说,似乎两者都应该具有完全相同的输出。在第二个示例中保证是字符串比较,就像在第一个示例中一样。
  • @Rob 比较是在一个通用方法中完成的,它知道它何时被编译它会进行字符串比较。因此,泛型方法中使用的== 运算符是object == 运算符,而不是string== 运算符。 .Substring(1) 产生不同结果的原因是该方法创建了一个 new 字符串对象,而不是可能重用另一个已经具有相同值的对象。因此,由于两个具有相同值但不同对象的字符串被泛型方法视为不同。
【解决方案4】:

String.Equals 确实提供了重载来处理大小写和文化感知比较。如果您的代码没有使用这些,开发人员可能只是习惯于 Java,其中(正如 Matthew 所说),您必须使用 .Equals 方法进行内容比较。

【讨论】:

  • 这应该会更高,因为文化感知(和明确的大小写敏感)比较更实用 - 例如docs.microsoft.com/en-us/dotnet/standard/base-types/…
  • 同意@Bartosz!明确您的意图是 Microsoft 的最佳实践。使用 == 并没有明确告诉读者在区分大小写和文化方面预期会发生什么。
【解决方案5】:

这两种方法在功能上的作用相同——它们比较
正如 MSDN 上所写:

但如果您的字符串实例之一为空,则这些方法的工作方式不同:

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");

【讨论】:

【解决方案6】:

this article 上有一篇文章,您可能会觉得这很有趣,其中引用了 Jon Skeet 的一些话。好像用法差不多。

Jon Skeet 指出,实例 Equals 的性能“在字符串较短时稍好——随着字符串长度的增加,这种差异变得完全不显着。”

【讨论】:

  • 什么才是“短”字符串?
  • @AndHeCodedIt 取决于你的系统的速度我猜。运行基准测试。
【解决方案7】:

我想补充一点,还有另一个区别。这与 Andrew 发布的内容有关。

这也与在我们的软件中发现一个非常烦人的错误有关。请参阅以下简化示例(我也省略了空检查)。

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 比较不同类型的枚举。在意识到这是错误的原因之前,您将阅读很多次。特别是如果SPECIAL_NUMBER 的定义不在问题区域附近。

这就是为什么我真的反对在没有必要的情况下使用 Equals。你失去了一点类型安全性。

【讨论】:

  • 这就是为什么最好使用静态 string.Equals 方法总是带有 StringComparison 参数 - 尝试比较字符串和整数时重载会产生编译错误
  • 我希望方法和运算符参数可以具有不允许隐式转换、向上转换、装箱转换或上述组合的属性(并且 EqualsReferenceEquals 一直使用它们)。我还希望 == 以外的其他运算符用于引用相等。让Equals== 定义与它们编译的所有参数类型的等价关系比让它们在不产生等价关系的情况下编译更有帮助。
【解决方案8】:

我一直在努力解决错误,因为我阅读了此页面并得出结论,实际上没有任何有意义的区别,因此我将在此处发布此链接以防其他人发现它们从 == 和 equals 中得到不同的结果。

Object == equality fails, but .Equals succeeds. Does this make sense?

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

【讨论】:

  • 据我了解,第二次相等检查失败,因为 Object == Object 解析为 System.Object.ReferenceEqualsab 是内存中的两个不同对象。其余的比较字符串对象的值。即使是最后一个比较,似乎为Object 类型对象调用.Equals,因为它是作为virtual 方法实现的,它默认调用String.Equals
猜你喜欢
  • 2019-01-20
  • 1970-01-01
  • 2018-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多