为什么C#不能将两种对象类型相互比较,而VB不能呢?


152

我在C#中有两个对象,不知道它是布尔型还是任何其他类型。但是,当我尝试比较时,这些C#无法给出正确的答案。我已经用VB.NET尝试了相同的代码,并且做到了!

如果有解决办法,谁能告诉我该如何解决?

C#:

object a = true;
object b = true;
object c = false;
if (a == b) c = true;
MessageBox.Show(c.ToString()); //Outputs False !!

VB.NET:

Dim a As Object = True
Dim b As Object = True
Dim c As Object = False
If (a = b) Then c = True
MessageBox.Show(c.ToString()) '// Outputs True

3
如果将相等比较器更改为该a.Equals(b)怎么办?
杰森·梅克利

8
对于教学目的,这是一个很好的问题。
路易(Lobo)2013年

10
因为您的VB.NET代码不等于您的C#代码。
安全猎犬

9
分配给a您时,将得到装箱并创建一个包含的框true。当您分配给b您时,还会得到另一个包含的框true。比较a和时b,由于两者都是编译时类型object,因此您将调用operator ==(object, object)C#语言规范定义的重载。此重载检查引用是否到达同一对象。由于您有两个框,结果为false,并且语句“在...之下” if将不会运行。为了更好地理解这一点,请尝试将其分配b给它:object b = a;现在只有一个框。
Jeppe Stig Nielsen

3
我之前曾有机会说过“请注意,假设VB.NET和C#是同一种语言,但口音不同,但不是”
AakashM 2013年

Answers:


168

在C#中,==运算符(当应用于引用类型表达式时)执行引用相等性检查,除非它已重载。您正在比较装箱转换结果的两个引用,因此它们是不同的引用。

编辑:使用重载的类型==,您可以得到不同的行为-但这是基于表达式的编译时类型。例如,string提供==(string, string):

string x = new string("foo".ToCharArray());
string y = new string("foo".ToCharArray());
Console.WriteLine(x == y); // True
Console.WriteLine((object) x == (object) y); // False

在这里,第一个比较使用的是重载运算符,但是第二个使用的是“默认”引用比较。

在VB中,=运算符object.Equals(x, y)要做很多工作-它甚至不仅仅等同于使用,因为诸如此类的事情Option Compare会影响文本的比较方式。

根本上讲,操作员的工作方式不同,也不打算以相同的方式工作。


17
+1我知道你会在身边,你喜欢这些神秘的问题:)
Abdusalam Ben Haj 2013年

3
@AbZy:我希望能够提供有关=VB 中内容的更详细的解释,但是该规范尚不清楚。
Jon Skeet

有趣的事情,但将对象更改为动态的行为与VB相同
VladL

4
@VladL:是的,因为这样它将按执行时类型进行bool == bool比较。
Jon Skeet

1
@Mahdi Lobo可能提供了代码,但他的回答也不对,与Jon的不同。
Servy

79

除了乔恩(Jon)的答案可以解释C#方面之外,这也是VB的作用:

在VB中Option Strict On,通过进行的比较= 始终会测试值的相等性,而不会测试引用的相等性。实际上,切换后您的代码甚至无法编译,Option Strict On因为System.Object未定义Operator=。您应该始终启用此选项,它比维纳斯捕蝇器更有效地捕获错误(尽管在您的特殊情况下,这种松懈的行为实际上可以做对了)。1个

实际上,使用Option Strict On,VB的行为甚至比C#更为严格:在C#中,a == b 要么触发调用,要么SomeType.operator==(a, b)如果调用不存在,则调用引用相等性比较(等效于object.ReferenceEquals(a, b))。

另一方面,在VB中,比较a = b 总是调用相等运算符。2如果要使用引用相等比较,则必须使用a Is b(再次与相同Object.ReferenceEquals(a, b))。


1)这充分说明了使用为什么Option Strict Off是个坏主意:从.NET正式发布之前到几年前,我已经使用VB.NET已有近十年了,我绝对不知道该怎么a = bOption Strict Off。它进行了某种相等性比较,但是究竟发生了什么以及为什么发生,没有任何想法。但是,它比C#的dynamic功能复杂(因为它依赖于文档齐全的API)。这是MSDN所说的:

因为Option Strict On提供强类型,防止意外类型转换而导致数据丢失,不允许后期绑定并提高性能,所以强烈建议使用它。

2)乔恩(Jon)提到了一个例外,即字符串,出于向后兼容的原因,相等比较会做更多的事情。


4
+1。我认为这是一种情况,VB.NET的设计人员成功地使这种语言“适用于”来自VB6和VBA的程序员,因为OOP不太重要,因此引用相等性的概念就不那么重要了。VB编码器可以编写良好的工作代码,而无需过多考虑对象等。
John M Gant

5
+1这并没有得到应有的重视。不使用Option Strict On必须被视为刑事犯罪……
Deer Hunter

1
@JohnMGant:不了解引用身份的重要性的编码人员也许可以编写碰巧可用的代码,但不太可能真正知道可以安全地更改哪些内容,哪些更改将永远破坏这些内容以及哪些更改可能似乎可以正常工作,但会引起不想要的令人讨厌的副作用(例如,导致应该将对具有相同状态的不同可变对象的引用改为对同一对象的引用)。如果很少更改对象,则这种更改可能不会立即引起任何问题,但可能会使以后难以发现的错误产生。
超级猫

4

对象实例不与运算符“ ==”进行比较。您应该使用方法“等于”。使用“ ==”运算符比较参考,而不是对象。

试试这个:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }
}

MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

结果:

a reference is not equal to b reference
a object is not equal to b object

现在,尝试以下方法:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }

    public bool Equals(MyObject o)
    {
        return (Value.CompareTo(o.Value)==0);
    }
}
MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

结果:

a reference is not equal to b reference
a object is equal to b object

1
这仅仅是因为您没有覆盖operator ==。如果您覆盖该运算符并且不等于,那么您的输出将被反转。比较参考中operator ==没有固有的内容,比较中的值也没有固有的内容Equals。它们只是确定平等的两种方式。两者都具有参考比较的默认实现,并且两者都可以被覆盖以执行您希望它们执行的任何操作。唯一的不同是Equals虚拟的,operator ==不是虚拟的。
Servy

1
@Servy:请注意,您不能覆盖 == -您只能重载它。
Jon Skeet

1
对不起,-1。这个答案是完全不正确的,不应被接受。
Konrad Rudolph

某个地方有一个Java问题正在等待该答案。
乍得Schouggins

3

问题在于,C#中的==运算符是基于两个参数的编译时类型对静态方法的调用(嗯,也许从技术上来说不是,但可以这样)。这些对象的实际运行时类型是什么都没有关系。

基于该编译时间类型,编译器将确定operator ==要使用的实现。它可能使用默认object实现,可能使用该语言提供的数字重载之一,也可能是用户定义的实现。

这与VB的不同之处在于,VB不会在编译时确定实现。它一直等到运行时,然后检查给出的两个参数,以确定==应使用哪个操作员实现。

您的代码包含布尔值,但它们位于类型为的变量中object。因为变量是type object,所以C#编译器使用的object实现==,该实现比较引用而不是对象实例。由于布尔值是框,因此即使它们的值相同,它们也没有相同的引用。

VB代码不在乎变量是什么类型。它一直等到运行时,然后检查这两个变量,发现它们实际上都是布尔类型,因此使用布尔==运算符实现。该实现将比较布尔值,而不是它们的引用(并且布尔值将在调用调用该运算符之前取消装箱,因此引用比较不再有意义)。因为布尔值相同,所以它返回true。


对于C#来说看起来不错;我对=VB中的确切含义还不够了解。
乔恩·斯基特

@JonSkeet还算公平。
Servy

msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx,在部分“无类型编程与关系比较运算符”:=与其他所有的关系比较运营商,如沿<>=等等。当操作者双方或双方都在时,将给予特殊待遇Object。进行这种特殊处理是为了使习惯使用Variant.NET VB之前的类型的VB6程序员能够以Object以前使用的方式在VB.Net中使用Variant
rskar 2013年

换句话说,不考虑重载和的影响Option Strict On,VB =偏向于取消对的装箱,Object直到可以到达字符串或数字为止。
rskar 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.