==和等于()之间的C#差异

Dra*_*cir 519 .net c# equals

我在Silverlight应用程序中有一个比较2个字符串的条件,由于某种原因,当我使用==它时返回false.Equals()返回true.

这是代码:

if (((ListBoxItem)lstBaseMenu.SelectedItem).Content.Equals("Energy Attack"))
{
    // Execute code
}

if (((ListBoxItem)lstBaseMenu.SelectedItem).Content == "Energy Attack")
{
    // Execute code
}
Run Code Online (Sandbox Code Playgroud)

任何理由为什么会这样?

Meh*_*ari 410

==在表达式上使用时object,它将解析为System.Object.ReferenceEquals.

Equals只是一种virtual方法并且行为如此,因此将使用被覆盖的版本(对于string类型比较内容).

  • 除非运营商在班级中具体实施 (52认同)
  • @DominicCronin这不是真的.即使在类中实现了==,它也会被忽略,因为比较左边的类型是object.看起来运算符重载是在编译时确定的,在编译时它只知道左侧是一个对象. (21认同)
  • 要清楚,`object`类型(注意等宽字体)在技术上意味着是"类型为System.Object"的表达式.它与表达式引用的实例的运行时类型无关.我认为"用户定义的运算符被视为'虚拟'方法"这一陈述极具误导性.它们被视为重载方法,仅依赖于操作数的编译时类型.实际上,在计算了候选用户定义的运算符集之后,其余的绑定过程将完全是方法重载解析算法 (7认同)
  • @DominicCronin我相信你的第一个语句是正确的,因为==将解析为object,但你的第二个语句是运算符重载以类似的方式解析.它们是完全不同的,这就是为什么.Equals将解析为字符串而==将解析为对象. (4认同)
  • @DominicCronin误导性部分是"虚拟"方法解决方案取决于实例的实际运行时类型,而在运算符重载解析中_mmpletely_忽略,这确实是我的答案的全部要点. (4认同)
  • @xagyg 它是“字符串”类型的“重载”。尝试: `Console.WriteLine((object)a == (object)b)` 其中 `a` 和 `b` 是字符串,结果将与 `ReferenceEquals` 相同。类似地,如果你在你自己的类型中声明了一个重载的 `==` 运算符,你可以模仿 `string` 的相同行为。 (2认同)
  • @Jai - 出色的观察,但与此处讨论的内容有些不同,您的结论是错误的。通常使用 `(string) == (string)`,这意味着使用 `String` 的 `==` 重载。该过载的行为符合预期。*这*是`==`“适用于大多数情况”的原因。即使比较具有相同内容的两个不同字符串对象。在所讨论的问题中,表达式 **types** 是 `(object) == (string)`。正如 Mehrdad 在他的回答中所说,这会导致使用 **object's** `==`。 (2认同)

Blu*_*kMN 293

将对象引用与字符串进行比较时(即使对象引用引用字符串),将==忽略特定于字符串类的运算符的特殊行为.

通常(当不处理字符串时),Equals比较,同时==比较对象引用.如果您要比较的两个对象是指对象的同一个确切实例,则两者都将返回true,但如果一个具有相同的内容并来自不同的源(是具有相同数据的单独实例),则只有Equals将返回true.但是,正如注释中所指出的,string是一种特殊情况,因为它会覆盖==运算符,因此当纯粹使用字符串引用(而不是对象引用)进行处理时,即使它们是单独的实例,也只会比较这些值.以下代码说明了行为的细微差别:

string s1 = "test";
string s2 = "test";
string s3 = "test1".Substring(0, 4);
object s4 = s3;
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s2), s1 == s2, s1.Equals(s2));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s3), s1 == s3, s1.Equals(s3));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s4), s1 == s4, s1.Equals(s4));
Run Code Online (Sandbox Code Playgroud)

输出是:

True True True
False True True
False False True
Run Code Online (Sandbox Code Playgroud)

  • 发现.'=='运算符比较对象引用(浅比较),而.Equals()比较对象内容(深度比较).正如@mehrdad所说,.Equals()被覆盖以提供深度内容比较. (8认同)
  • 史诗代码示例+1,这让我理解这一点.显示静态类型(左手侧类型)为对象的一般情况,以及静态类型(/ RHS类型)为字符串的特定情况.并且很好地接触了弦乐实习. (6认同)
  • 当多个字符串文字相同时,编译器足够聪明,可以为两个引用使用相同的地址,因为 .NET 中的字符串是不可变的。 (5认同)
  • 当然String实现了自定义==运算符.如果没有那么使用==将不会比较内容.所以String在这里使用是一个不好的例子,因为它无法帮助我们理解没有定义自定义运算符的一般情况. (4认同)
  • 好的,但我仍然不明白为什么 object.ReferenceEquals(s1, s2) 返回 true 。它是如何工作的?在内存中它们是不同的对象,那么为什么引用比较返回 true 呢?即使将引用替换为: string s1 = "test"; 字符串 s2 = "tes" + "t"; (3认同)
  • @rory.ap 如果你查看这个答案的编辑历史,你会发现它的措辞在相当长一段时间内都以不那么吸引人的方式开始,并以不准确的解释开始(即使它被标记为不准确,但这可能不是最好的方法)开始)。这可能让很多人感到困惑。 (3认同)
  • 我将把这篇文章留在这里,因为我认为强调“没有”发生的事情很有价值,因为你必须密切关注才能意识到它。(而且我认为展示正确和错误理解的代码也是值得的。)我希望评分不会低于 0。 (2认同)
  • @badsamaritan因为字符串实习 (2认同)

Jar*_*Par 45

==并且.Equals都依赖于实际类型中定义的行为和呼叫站点的实际类型.两者都只是方法/运算符,可以在任何类型上重写,并给出作者所希望的任何行为.根据我的经验,我发现人们.Equals在对象上实现但忽略了实现运算符是很常见的==.这意味着.Equals实际上==将测量值的相等性,同时测量它们是否是相同的参考.

当我使用一种新的类型,其定义是不变的或编写通用算法时,我发现最佳实践如下

  • 如果我想比较C#中的引用,我Object.ReferenceEquals直接使用(在通用情况下不需要)
  • 如果我想比较我使用的值 EqualityComparer<T>.Default

在某些情况下,当我觉得使用==不明确时,我会Object.Reference在代码中明确使用equals来消除歧义.

Eric Lippert最近做了一篇关于为什么CLR中有两种平等方法的博客文章.值得一读

  • 这不完全正确.==无法覆盖,它是一种静态方法.它只能超载,这是一个重要的区别.因此,为==运算符执行的代码在编译时链接,而Equals是虚拟的并在执行时找到. (10认同)

Col*_*nic 19

首先,有有差别.对于数字

> 2 == 2.0
True

> 2.Equals(2.0)
False
Run Code Online (Sandbox Code Playgroud)

对于字符串

> string x = null;
> x == null
True

> x.Equals(null)
NullReferenceException
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,==行为都比.Equals

  • 我不确定我是否认为使用 `==` 运算符将整数类型强制转换为浮点类型是一件好事。例如,16777216.0f 是否应该等于 (int)16777217、(double)16777217.0、两者或两者都不相等?整数类型之间的比较很好,但浮点比较应该只用显式转换为匹配类型的值执行恕我直言。将“float”与“float”以外的其他内容进行比较,或将“double”与“double”以外的其他内容进行比较,我认为这是一种主要的代码异味,在没有诊断的情况下不应编译。 (3认同)

Mik*_*lls 13

我想补充一点,如果你将对象转换为字符串,那么它将正常工作.这就是为什么编译器会给你一个警告说:

可能的意外参考比较; 得到一个值比较,左侧输入'string'


kas*_*hif 13

==运算符 1.如果操作数是值类型且它们的值相等,则返回true,否则返回false.2.如果操作数是参考类型而不是字符串,并且两者都引用同一个对象,则返回true,否则返回false.3.如果操作数是字符串类型且它们的值相等,则返回true,否则返回false.

.Equals 1.如果操作数是引用类型,它执行引用相等,如果两者都引用同一个对象,则返回true,否则返回false.2.如果操作数是值类型,那么与==运算符不同,它首先检查它们的类型,如果它们的类型相同则执行==运算符,否则返回false.

  • 这是不正确的.`==`运算符可以为任何类型重载,而不仅仅是字符串.仅为字符串描述特殊情况异常会错误地表示运算符的语义.如果操作数是引用类型,如果操作数引用相同的对象,则返回true,这将更准确,尽管可能不是非常有用,除非存在适用的重载,在这种情况下,该重载的实现决定了结果"."Equals"也是如此,它增加了复杂性,它是一个虚方法,所以它的行为可以被覆盖也可以被重载. (2认同)

Lir*_*mir 13

据我所知,答案很简单:

  1. ==比较对象引用.
  2. .Equals比较对象内容.
  3. String数据类型总是像内容比较一样.

我希望我是对的,它回答了你的问题.


Mar*_*que 5

因为.Equal到目前为止还没有提到该方法的静态版本,我想在这里添加这个以总结和比较 3 个变体。

MyString.Equals("Somestring"))          //Method 1
MyString == "Somestring"                //Method 2
String.Equals("Somestring", MyString);  //Method 3 (static String.Equals method) - better
Run Code Online (Sandbox Code Playgroud)

whereMyString是来自代码中其他地方的变量。

背景信息和总结:

在 Java 中==不应使用用于比较字符串。如果您需要同时使用这两种语言,我会提到这一点,并且让您知道 using==也可以用 C# 中更好的东西代替。

在 C# 中,使用方法 1 或方法 2 比较字符串没有实际区别,只要它们都是字符串类型。但是,如果一个为空,一个是另一种类型(如整数),或者一个表示具有不同引用的对象,那么,如最初的问题所示,您可能会遇到比较内容是否相等可能不会返回什么你期待。

建议的解决方案:

因为 using==.Equals比较事物时的using并不完全相同,所以您可以改用静态 String.Equals方法。这样,如果两侧的类型不同,您仍然会比较内容,如果一个为空,您将避免异常。

   bool areEqual = String.Equals("Somestring", MyString);  
Run Code Online (Sandbox Code Playgroud)

写的多一点,但在我看来,使用起来更安全。

以下是从 Microsoft 复制的一些信息:

public static bool Equals (string a, string b);
Run Code Online (Sandbox Code Playgroud)

参数

a 细绳

要比较的第一个字符串,或null.

b 细绳

要比较的第二个字符串,或null

退货 Boolean

true如果 的值a与 的值相同b;否则,false。如果ab都是null,则该方法返回true


Ole*_*ers 5

作为对已经很好的答案的补充:此行为不仅限于字符串或比较不同的数字类型。即使两个元素都是相同基础类型的 object 类型。“==”不起作用。

以下屏幕截图显示了比较两个对象 {int} - 值的结果

来自 VS2017 的示例