我似乎偶然发现了C#IF语句给出错误结果的情况。我试图写一个版本的Equals()来深入比较一个类的两个实例。
这是一个带有一些调试的简单测试用例:
namespace IfTest
{
class MyClass
{
public string String1
{ get; set; }
public int Int1
{ get; set; }
public float Float1
{ get; set; }
public bool Bool1
{ get; set; }
public MyClass(string s, int i, float f, bool b)
{
String1 = s;
Int1 = i;
Float1 = f;
Bool1 = b;
}
public override bool Equals(object otherInstance)
{
bool isEqual = true;
MyClass other = (MyClass)otherInstance;
int good = 0, bad = 0;
// Compare everything in 'other' to 'this', using reflection
Type sourceType = other.GetType();
Type destinationType = this.GetType();
foreach (PropertyInfo sourceProperty in sourceType.GetProperties())
{
PropertyInfo destinationProperty = destinationType.GetProperty(sourceProperty.Name);
if (destinationProperty == null)
{
Console.WriteLine("Destination {0} is null", sourceProperty.Name);
isEqual = false;
bad++;
}
else
{
var x = sourceProperty.GetValue(other);
var y = destinationProperty.GetValue(this);
//if (sourceProperty.GetValue(other, null) != destinationProperty.GetValue(this, null))
if (x != y)
{
Console.WriteLine("Name {0}: {1} {2} different", sourceProperty.Name, x, y);
isEqual = false;
bad++;
}
else
{
Console.WriteLine("Name {0}: {1} {2} same", sourceProperty.Name, x, y);
good++;
}
}
}
Console.WriteLine("Good: {0}. Bad {1}", good, bad);
return isEqual;
}
}
}
using System;
namespace IfTest
{
class Program
{
static void Main(string[] args)
{
MyClass a = new MyClass("abc", 23, 45.67F, false);
MyClass b = new MyClass("abc", 23, 45.67F, false);
// Test IF usually works with var's
var i = a.Int1;
var j = b.Int1;
Console.WriteLine("Main test {0}",
(i == j) ? "OK" : "Fail");
a.Equals(b);
Console.ReadLine();
}
}
}
在MyClass.Equals()中我注释掉了这一行
if (sourceProperty.GetValue(other, null) != destinationProperty.GetValue(this, null))
并将2个属性值放在临时变量中。
运行此命令:
Main test OK
Name String1: abc abc same
Name Int1: 23 23 different
Name Float1: 45.67 45.67 different
Name Bool1: False False different
Good: 1. Bad 3
表明数字类型的IF失败。如果我将x和y分配更改为:
var x = sourceProperty.GetValue(other);
var y = sourceProperty.GetValue(other);
我可以通过添加以下内容来解决这个问题:
if (x is int)
{
if ((int) x == (int)y)
Console.WriteLine("INT Name {0}: {1} {2} same", sourceProperty.Name, x, y);
}
但我必须测试每种数字类型。
这是一个C#问题还是我做了些傻话?我使用Visual Studio Express 2013 for Desktop版本12.0.31101.00 Update 4 .Net版本4.5.50938和Visual C#2013。
答案 0 :(得分:5)
比较失败是因为您在==
类型上使用Object
运算符 - 它执行引用相等操作而不是值相等操作,此外还有.NET中的反射API Framework不会缓存并重新返回预生成的反射对象实例,这就是为什么返回false:
foo.GetType().GetMethod("Bar") == foo.GetType().GetMethod("Bar")
另一个问题是你不恰当地使用var
关键字:它隐藏了你正在装箱值类型的事实。 x
和y
的类型是装箱的 - int
为Object
,因此它执行引用相等比较而不是值相等。
解决方案是通过强制转换为实际类型,在引用类型和unbox值类型上显式调用.Equals
方法:
foo.GetType().GetMethod("Bar").Equals( foo.GetType().GetMethod("Bar") )
Int32 x = (Int32)sourceProperty.GetValue(other);
答案 1 :(得分:2)
当您致电GetValue
时,您将获得object
。如果值为int
,则int
被装箱为object
,然后返回。现在,当您使用==
比较两个盒装整数时,会使用==
类中的object
运算符。当您转换为int
时,它现在使用==
中的int
运算符。正如您所发现的那样,这些做了两件不同的事情。
返回false:
Console.WriteLine("{0}", (object)5 == (object)5);
返回true:
Console.WriteLine("{0}", ((object)5).Equals((object)5));
您可以使用.Equals()
方法进行相等性检查。
答案 2 :(得分:1)
如果你知道你在比较什么,结果是有意义的。
如果您指定变量类型而不是使用var
关键字,您会知道变量是对象:
object x = sourceProperty.GetValue(other);
object y = destinationProperty.GetValue(this);
无论您使用反射阅读的属性类型如何,GetValue
方法的返回值为object
。
对于参考类型,变量值将是参考。对于值类型,变量值将是对值进行装箱的对象的引用。
对于参考类型,比较有效,因为变量直接指向对象。对于字符串,将找到并使用方法String.Equals
。对于值类型,装入值的对象没有值的相等比较器,因此将使用Object.Equals
方法,该方法比较对象的引用,而不是对象中的值。
这可以通过一个更简单的例子来显示:
object x = 1;
object y = 1;
Console.WriteLine(x == y);
输出:
False
尽管对象包含相同的盒装值,但比较返回false
,因为引用不同。
答案 3 :(得分:0)
您的代码错误,因为GetValue返回一个对象,因此返回两个不同的对象
https://msdn.microsoft.com/en-us/library/b05d59ty%28v=vs.110%29.aspx
你必须首先使用GetValue,然后如果它是casecase就投出它并比较两者(它将是值类型)