我在Silverlight应用程序中有一个比较2个字符串的条件,出于某种原因,当我使用==时,它返回false,而.Equals()返回true。
代码如下:
1 2 3 4 5 6 7 8 9
| if (((ListBoxItem)lstBaseMenu.SelectedItem).Content.Equals("Energy Attack"))
{
// Execute code
}
if (((ListBoxItem)lstBaseMenu.SelectedItem).Content =="Energy Attack")
{
// Execute code
} |
为什么会这样?
- See also:stackoverflow.com/questions/144530/or-equals
- 但操作者并不是多晶型的。在这一代码中,运算符被引用在EDOCX1&2类型上,其中一个值的特征比较。
- 在@drewnoakes怎么样:编译器选择EDOCX1&0>的基础上,根据操作者的编译时间类型过载。财产是object运算符不是虚拟的,因此缺陷的实现被呼唤,给出了一个参考平等的比较。用同样的方法,呼叫的虚拟object.Equals(object);stringOverrides this method and performs an ordinal comparison on on the string content.See MSDN.Microsoft.com/en-US/Library/FKFD9EH8(V=VS.110).Aspx and Referencesource.Microsoft.com/ 350;MSCORLIB/System/STRING.CS,507&ZWNJ&\\\
- @Phoog's explanation is precise.应当指出,当左手侧的==有编译时间型EDOCX1〕〔2〕时,右手侧有编译时间型string;编译器必须从中找出(问题1)超载operator ==(object, object);但这将是一个能够编译的问题。所以读了《汇编时光警报》为确定问题和继续使用==,将左手放在string边。如果我记忆正确的话,警报文本就是这样建议的。
- @Jeppestignielsen+1 for the advice to read compiler warnings.甚至更好:转向警戒——作为错误的选择,强迫每个人付钱给他们。
当==用于object类型的表达式时,它将解析为System.Object.ReferenceEquals。
Equals只是一个virtual方法,它的行为是这样的,因此将使用重写版本(对于string类型,它比较内容)。
- 除非在类中特别实现了运算符
- @多米尼克,这不是真的。即使在类中实现了==,也将忽略它,因为比较左侧的类型是对象。看起来,运算符重载是在编译时确定的,而在编译时,它只知道左侧是一个对象。
- @在问题中,mikekulls返回的类型。content是object,所以是。但是,Mehrdad说的是"对象类型",而不是"对象类型的引用"。我的解释是,他指的是引用类型,==的实现将解析为对象上的类型。尽管如此,运算符重载的解析方式与虚拟方法类似。
- @Dominiccronin我相信您的第一条语句是正确的,因为==将解析为对象,但您的第二条语句(即运算符重载以类似方式解析)则不是。它们完全不同,这就是为什么.equals将解析为字符串,而==将解析为对象。
- 明确地说,object类型(注意单空间字体)在技术上是指"System.Object类型的表达式"。它与表达式引用的实例的运行时类型没有任何关系。我认为"用户定义的运算符被视为virtual方法"这一说法极为误导。它们被视为重载方法,并且只依赖于操作数的编译时类型。实际上,在计算出一组候选的用户定义操作符之后,绑定过程的其余部分将完全是方法重载解析算法。
- @Mehrdadafshari也许我应该说,运算符重载的解决方式与其他重载类似。也许我说的是不精确的术语,但在这种情况下并不是"极度误导"。@Mikekulls我刚刚编写了测试这个的代码,我可以确认如果您实现了一个操作符重载,它将被使用。是的-当然,签名必须匹配,在这种情况下,这意味着您需要对public static operator ==(object a, Foo b){...}或其他代码进行编码,但重载运算符本质上与重载方法非常相似。
- @Dominiccronin令人误解的部分是,virtual方法解析取决于实例的实际运行时类型,而在运算符重载解析中,这完全被忽略了,这确实是我答案的全部要点。
- 所以为什么string a = String.Copy("xyz"), b = String.Copy("xyz"); Console.WriteLine(String.ReferenceEquals(a,b)); Console.WriteLine(a==b);打印false,然后是true。似乎==和string的Equals一样?
- @Xagyg对于string类型是"过载"。试一试:Console.WriteLine((object)a == (object)b),其中a和b是字符串,其结果与ReferenceEquals相同。同样,如果您在自己的类型中声明了一个重载的==操作符,则可以模拟string的相同行为。
- @cosmin你的编辑是完全无效的,把正确的答案变成了错误的答案。
- 这个答案很好,但也要注意字符串是不可变的。如果我没有弄错,如果您执行字符串文本(例如string str ="Hello";),那么"hello"实例将保存在内存中的某个位置,并且进一步的"hello"字符串文本将不会创建新实例-它将引用内存中的该实例。这正是==在大多数情况下起作用的原因。当你有了stringbuilders和ui文本时,事情就开始失控了。
- 通过指出listem.content的类型是object,所以==解析为==(object,object)而不是==(string,string),可以更清楚地说明这个答案。
- @JAI-非常好的观察,但是与这里讨论的有所不同,并且您的结论是错误的。通常情况下,使用(string) == (string),这意味着使用String的==过载。这个重载行为如预期的那样。这就是=="适用于大多数情况"的原因。即使比较两个内容相同的不同字符串对象。在讨论的问题中,表达式类型是(object) == (string)。这会导致使用对象的==,正如mehrdad在回答中所说。
- @杰奥,我想澄清一下,你的观察有时是正确的。不是在"大多数情况下",而是在编译器看到类型(object)而不是类型(string)的情况下。例如,取两个字符串,将它们存储在声明为object的变量中,然后比较这两个变量。在这种情况下,它编译为object.==,并作为参照等价物实施。所以只有当它是同一个实例时才是真的。
将对象引用与字符串进行比较时(即使对象引用引用了字符串),特定于字符串类的==运算符的特殊行为将被忽略。
通常(在不处理字符串时,也就是说,Equals比较值,而==比较对象引用。如果要比较的两个对象引用的是同一个对象的同一个精确实例,则这两个对象都将返回true,但如果一个对象具有相同的内容并且来自不同的源(是具有相同数据的单独实例),则只有equals将返回true。但是,正如注释中所指出的,字符串是一种特殊情况,因为它会重写==运算符,这样,在纯处理字符串引用(而不是对象引用)时,即使这些值是单独的实例,也只会比较这些值。下面的代码说明了行为上的细微差别:
1 2 3 4 5 6 7
| 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)); |
输出是:
1 2 3
| True True True
False True True
False False True |
- 当场。"=="运算符比较对象引用(浅比较),而.equals()比较对象内容(深比较)。正如@mehrdad所说,.equals()被重写以提供深度内容比较。
- 我将把这篇文章留在这里,因为我认为强调没有发生的事情是很有价值的,因为你必须密切关注才能意识到这一点。(我认为证明正确和错误理解的代码也是值得的。)我希望评级不会低于0。
- 当然,字符串实现了一个自定义的==运算符。如果没有,那么使用==将不会比较内容。所以在这里使用字符串是一个不好的例子,因为它不能帮助我们理解没有定义自定义操作符的一般情况。
- +1对于史诗代码示例,这让我理解了这一点。显示静态类型(左侧类型)为对象的一般情况,静态类型(rhs类型)的特定情况为字符串。而且还可以很好地接触到绳子。
- 我认为最好先说明它做了什么,然后再解释(重写)它不做什么。现在,撇油器很容易把错误答案读作正确答案。
- 好吧,但我还是不明白为什么object.referenceequals(s1,s2)返回true。它是如何工作的?在内存中,它们是不同的对象,所以为什么引用比较返回true?即使将引用替换为:string s1="tes t";string s2="tes"+"t";
- 当多个字符串文本相同时,编译器足够聪明,可以为两个引用使用相同的地址,因为.NET中的字符串是不可变的。
- @因为弦乐节间的缘故
==和.Equals都取决于实际类型和调用站点的实际类型中定义的行为。这两者都只是方法/运算符,可以在任何类型上被重写,并给出作者希望的任何行为。根据我的经验,人们在一个对象上执行.Equals,但忽略了执行操作符==,这是很常见的。这意味着.Equals将实际测量值的相等性,而==将测量值是否相同。
当我使用一个定义为flux或编写通用算法的新类型时,我发现最佳实践如下
- 如果我想比较c中的引用,我直接使用Object.ReferenceEquals(一般情况下不需要)
- 如果我想比较值,我使用EqualityComparer.Default。
在某些情况下,当我觉得==的用法不明确时,我会在代码中显式地使用Object.Referenceequals来消除歧义。
埃里克·利珀特最近发表了一篇关于为什么在CLR中存在两种平等方法的博客文章。值得一读
- http://blogs.msdn.com/ericlippet/archive/2009/04/09/double-your-dispatch-double-your-fun.aspx
- 很好,Jared,你直接违反了Jeff著名的"这里最好的代码就是没有代码"这真的有道理吗?另一方面,我可以看到这是从何而来,以及为什么需要将语义显式化。对于这种情况,我非常喜欢VB处理对象相等的方法。它简短明了。
- @康拉德,我真的应该说,"当我不熟悉一个类型,我发现最好的做法是以下。"是的,vb在这里有更好的语义,因为它真正区分了值和引用相等。C将两者混合在一起,有时会导致歧义错误。
- 这并不完全正确。==不能重写,它是静态方法。它只能超载,这是一个重要的区别。因此,为==运算符执行的代码在编译时链接,而equals是虚拟的,在执行时找到。
首先,这是有区别的。对于数字
1 2 3 4 5
| > 2 == 2.0
True
> 2.Equals(2.0)
False |
弦乐
1 2 3 4 5 6
| > string x = null;
> x == null
True
> x.Equals(null)
NullReferenceException |
在这两种情况下,==的行为比.Equals更有用。
- 我不确定是否将整型强制为使用==运算符的浮点类型是一件好事。例如,16777216.0f等于(int)16777217,(double)16777217.0,两者都等于还是都不等于?积分类型之间的比较很好,但是浮点比较只能在imho中使用显式转换为匹配类型的值执行。把float与float以外的东西或double与double以外的东西作比较,我觉得这是一种主要的代码味道,不需要诊断就不能编译。
- @Supercat我同意x == y并不意味着x/3 == y/3(尝试x = 5)和y = 5.0)这是令人痛心的。
- 我认为EDCOX1〔20〕对整数除法的使用是C语言和Java设计中的一个缺陷。Pascal的div甚至vb.net的` are much better. The problems with =`都更糟,尽管:x==y和y==z并不意味着x==z(考虑我之前评论中的三个数字)。至于你所说的关系,即使x和y都是float或都是double,x.equals((Object)y)并不意味着1.0f/x == 1.0f/y`(如果我有药物,它可以保证;即使==不区分正和零,Equals也应该如此)。
- 这很正常,因为equals()的第一个参数是字符串!
我还要补充一点,如果您将对象强制转换为字符串,那么它将正常工作。这就是为什么编译器会警告您说:
Possible unintended reference comparison; to get a value comparison,
cast the left hand side to type 'string'
- 确切地。@多米尼克:始终遵守编译时警告。如果有object expr = XXX; if (expr =="Energy") { ... },那么由于左侧是编译时类型object,编译器必须使用重载operator ==(object, object)。它检查参考相等性。这是否会给true或false带来好处,由于字符串间的相互作用,很难预测。如果您知道左侧是null或string类型,请在使用==之前将左侧铸造到string。
- 换一种说法。==(在确定是否使用引用相等或值相等时)取决于编译时类型/静态类型/左侧类型。(这是编译时分析中解析的类型)。而不是运行时类型/动态类型/rhs类型。BlueMonkmn的代码显示了这一点,尽管并没有使用Casting。
==算符1。如果操作数是值类型且其值相等,则返回true或false。2。如果操作数是引用类型(字符串除外),并且两者都引用同一对象,则返回true或false。三。如果操作数是字符串类型且其值相等,则返回true或false。
等于1。如果操作数是引用类型,则执行引用相等(即如果两个操作数都引用同一对象,则返回true或false)。2。如果操作数是值类型,则与==运算符不同,它首先检查其类型,如果它们的类型相同,则执行==运算符,否则返回false。
- 这是不正确的。==运算符可以重载任何类型,而不仅仅是字符串。只为字符串描述特殊情况异常会歪曲运算符的语义。更准确地说,"如果操作数是引用类型,如果操作数引用同一对象,则返回true,除非存在适用的重载,在这种情况下,该重载的实现将确定结果"。对于Equals,情况也是如此,但增加了一个复杂性,即它是一个虚拟方法,因此它的行为既可以被重写,也可以被重载。
据我所知,答案很简单:
==比较对象引用。
.equals比较对象内容。
字符串数据类型总是起到内容比较的作用。
我希望我是对的,它回答了你的问题。
因为到目前为止还没有提到.Equal方法的静态版本,所以我想在这里添加这个内容来总结和比较这3种变体。
1 2 3
| MyString.Equals("Somestring")) //Method 1
MyString =="Somestring" //Method 2
String.Equals("Somestring", MyString); //Method 3 (static String.Equals method) - better |
其中MyString是来自代码中其他地方的变量。
背景信息和摘要:
在使用EDCOX1的Java中,不应该使用2个比较字符串。我提到这个,以防你需要同时使用两种语言和让你知道使用==也可以用C语言中更好的东西来代替。
在C中,使用方法1或方法2比较字符串没有实际区别,只要两者都是字符串类型。但是,如果一个为空,一个为另一种类型(如整数),或者一个表示具有不同引用的对象,那么,正如最初的问题所显示的那样,您可能会体验到将内容与相等项进行比较可能不会返回您期望的结果。
建议的解决方案:
因为使用==与使用.Equals不完全相同,所以可以使用静态string.equals方法。这样,如果两侧的类型不同,您仍然可以比较内容,如果其中一个为空,则可以避免异常。
1
| bool areEqual = String.Equals("Somestring", MyString); |
写起来有点多,但在我看来,使用起来更安全。
以下是从Microsoft复制的一些信息:
1
| public static bool Equals (string a, string b); |
参数
a串
要比较的第一个字符串,或null。
b串
要比较的第二个字符串,或null。
返回Boolean。
如果a的值与b的值相同,则为true;否则为false。如果a和b都是null,则方法返回true。
在答案中再加一个点。
.EqualsTo()方法提供了与文化和区分大小写进行比较的方法。
@bluemonkmn之前的答案还有另一个维度。另外一个维度是@drahcir标题问题的答案,如所述,也取决于我们如何得出string值。举例说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| string s1 ="test";
string s2 ="test";
string s3 ="test1".Substring(0, 4);
object s4 = s3;
string s5 ="te" +"st";
object s6 = s5;
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s2), s1 == s2, s1.Equals(s2));
Console.WriteLine("
Case1 - A method changes the value:");
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));
Console.WriteLine("
Case2 - Having only literals allows to arrive at a literal:");
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s5), s1 == s5, s1.Equals(s5));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s6), s1 == s6, s1.Equals(s6)); |
输出是:
1 2 3 4 5 6 7 8 9
| True True True
Case1 - A method changes the value:
False True True
False False True
Case2 - Having only literals allows to arrive at a literal:
True True True
True True True |
我有点困惑。如果内容的运行时类型为string类型,则==和等于都应返回true。但是,由于看起来不是这样,所以运行时内容类型不是字符串,调用equals是在进行引用相等,这解释了equals("能量攻击")失败的原因。但是,在第二种情况下,应该调用overloaded==static运算符的决定是在编译时作出的,而这个决定似乎是==(string,string)。这向我建议内容提供到字符串的隐式转换。
- 你把它放回前面。对于一个开始等于("能量攻击")不会失败,==是返回错误的。==失败,因为它使用的是==From对象,而不是字符串。
- 默认情况下,运算符==通过确定两个引用是否指示同一对象来测试引用是否相等。因此,引用类型不必实现operator==以获得此功能。如果类型是不可变的,即不能更改实例中包含的数据,则重载运算符==以比较值相等而不是引用相等可能很有用,因为作为不可变对象,只要它们具有相同的值,就可以将它们视为相同的。在不可变类型中重写operator==不是一个好主意。
非常好的答案和例子!
我想补充一下两者的根本区别,
Operators such as == are not polymorphic, while Equals is
考虑到这个概念,如果您想出任何例子(通过查看左侧和右侧的引用类型,并检查/知道该类型是否实际具有==运算符重载和等于被重写),您一定会得到正确的答案。
c中的==标记用于两个不同的相等检查运算符。当编译器遇到该标记时,它将检查正在比较的任何一个类型是否为要比较的特定组合类型(*)或两个类型都可以转换到的类型组合实现了相等运算符重载。如果编译器发现这样的重载,它将使用它。否则,如果这两个类型都是引用类型,并且它们不是不相关的类(可能是接口,也可能是相关类),则编译器将把==视为引用比较运算符。如果这两个条件都不适用,编译将失败。
注意,其他一些语言对这两个相等性检查操作符使用单独的标记。例如,在vb.net中,=标记仅用于表达式中的可重载相等检查运算符,Is用作引用测试或空测试运算符。在不重写相等检查运算符的类型上使用=的操作将失败,而试图将Is用于除测试引用相等或无效之外的任何目的的操作也将失败。
(*)类型通常只重载相等以便与自身进行比较,但对于类型来说,重载相等运算符以便与其他特定类型进行比较可能很有用;例如,int可以(imho应该但不应该)定义相等运算符以便与float进行比较,这样16777217就不会端口本身等于16777216F。实际上,由于没有定义此类运算符,C会将int提升到float,在相等检查运算符看到之前将其四舍五入到16777216F;然后,该运算符看到两个相等的浮点数并将其报告为相等,而不知道发生了四舍五入。
- 与使用int-to-float比较返回false不同,我更喜欢f_使用的方法,即根本不允许这样的比较。然后程序员可以决定是否以及如何处理这些值具有不同类型的事实。因为有时,毕竟,我们确实想把3视为等同于3.0f。如果我们要求程序员说出每种情况下的意图,那么就没有导致意外结果的默认行为的危险,因为没有默认行为。
- @我个人的感觉是语言应该有它们的"正常"的平等测试手段来实现一个等价关系,并且禁止所有它不能实现的操作数组合。我不认为通过确认float精确地表示一个与int匹配的整数,而简单地禁止这种比较,在整数和float之间具有语言检查相等性的巨大优势,但是在比较之前,我认为任何一种方法都优于让语言执行有损转换。
当我们创建任何对象时,对象有两个部分:一个是内容,另一个是对该内容的引用。==比较了内容和参考文献;equals()只比较内容
http://www.codeproject.com/articles/584128/what-is-the-difference-between-equalsequals-and-eq
- 这不是真的。如果a和b都是字符串引用,那么a == b的结果不取决于引用是否指向同一对象。
=
==运算符可用于比较任何类型的两个变量,它只比较位。
1 2 3
| int a = 3;
byte b = 3;
if (a == b) { // true } |
注意:在int的左边有更多的0,但是我们不关心这个。
int a(00000011)==字节b(00000011)
记住==运算符只关心变量中位的模式。
如果两个引用(原语)引用堆上的同一对象,则使用==
无论变量是引用还是原语,规则都是相同的。
1 2 3 4 5 6 7
| Foo a = new Foo ();
Foo b = new Foo ();
Foo c = a ;
if (a == b ) { // false }
if (a == c ) { // true }
if (b == c ) { // false } |
A==C是真的A==B为假
A和C的位模式相同,因此使用==,它们是相等的。
相等():
使用equals()方法查看两个不同的对象是否相等。
例如,两个不同的字符串对象都表示"jane"中的字符
- 这是不正确的。考虑以下内容:object a = 3; object b = 3; Console.WriteLine(a == b);。输出为假,即使值的位模式相同。操作数的类型也很重要。在您的示例中,我们"不关心"不同数量的零的原因是,当我们调用equals运算符时,由于隐式转换,零的数量实际上是相同的。
equal和==之间的唯一区别在于对象类型比较。在其他情况下,例如引用类型和值类型,它们几乎相同(要么都是位相等,要么都是引用相等)。
对象:等于:位智能相等==:参考等式
string:(对于string,equals和==相同,但如果其中一个字符串更改为object,则比较结果将不同)等于:位智能相等==:位相等
请参阅此处了解更多说明。
- equals不一定要考虑按位相等。它是一个虚拟方法,重写可以做任何它想做的事情。
- 是的,你是对的,你可以做任何你想覆盖它的事情。但是我们讨论的主题是默认实现。object.equals的默认实现是位相等。