.NET执行==运算符时的转换处理
这个问题我最早发在CSDN论坛上,经过很多网友的热心帮助,最后我得出了一个结论。对不对我不知道,但是我有相当的自信是这样的,希望有人能告诉我吧。
例子代码如下:
运行这段代码,结果是什么呢?
Conversion occured
Conversion occured
True
刚开始我百思不得其解,因为d的值是5,而b的值是6,怎么会相等呢?后来在网友的帮助下,我得出了下面的看法:
首先,==运算符两边的两个操作数都是值类型,所以==运算符只比较其值是否相等即可。
然后,也是最重要的一点就是,==运算符两边的操作数的类型必须要一致,并且这个类型要重载了==运算符的实现。很显然,我自定义的Digit结构并没有重载==的实现。而据我推测,byte类型作为c#的基元类型,是实现了==运算符的。因此,在这里,CLR在执行的时候会将Digit转换到byte类型再比较,而转换的结果比转换前的值要大1,所以两者都是6了(注意转换后d的值还是5,但是==比较的是b和将d转换后的返回值)。结果就是True了。这也可以解释为什么会出现两个“Conversion occured”了。因为进行了两次隐式转换,为变量b赋值时进行了一次,==比较的时候又进行了一次。通过单步执行可以更清楚地看到这个过程。
为了更加深入研究这个例子,并且证明我前面的猜测:==运算符两边的操作数的类型必须要一致,并且这个类型要重载了==运算符的实现。我把代码做了一点小的修改,Main方法替换为下面的代码:
按照我的猜测,虽然==运算符两边都是Digit,而Digit又没有重载==运算符,但这时并不是不能比较,因为我们在Digit结构中定义了从Digit到byte的隐式转换。所以在执行到==的时候,会分别将d和d2转换到byte类型,再进行比较,即比较6和7了,最后结果当然就是:
Conversion occured
Conversion occured
False
如果我们把Digit定义中到byte的隐式转换注释掉,那么c#编译器就会报如下的错误:
Operator '==' cannot be applied to operands of type 'Digit' and 'Digit'
因为Digit没有重载==运算符,编译器又找不到存在的任何转换(包括隐式的和显式的),所以就会报错。
这个例子告诉我们,如果我们自己定义了结构,而且需要对该结构类型的对象进行==运算,那么我们就要自己定义==运算符,否则结果就可能不是我们预期的那样了:
这样,由于我们实现了在byte和Digit类型之间的==运算符,所以就不会像前面那样进行类型转换了,结果也当然是我们预期的那样了:
Conversion occured
False
例子代码如下:
using System;
using System.Collections;
struct Digit
{
byte value;
public Digit(byte value)
{
if (value > 9) throw new ArgumentException();
this.value = value;
}
public static implicit operator byte(Digit d)
{
Console.WriteLine("Conversion occured");
return (byte)((int)d.value + 1);
}
}
class app
{
static void Main()
{
Digit d = new Digit(5);
byte b = d;
Console.WriteLine(b == d);
}
}
using System.Collections;
struct Digit
{
byte value;
public Digit(byte value)
{
if (value > 9) throw new ArgumentException();
this.value = value;
}
public static implicit operator byte(Digit d)
{
Console.WriteLine("Conversion occured");
return (byte)((int)d.value + 1);
}
}
class app
{
static void Main()
{
Digit d = new Digit(5);
byte b = d;
Console.WriteLine(b == d);
}
}
Conversion occured
Conversion occured
True
刚开始我百思不得其解,因为d的值是5,而b的值是6,怎么会相等呢?后来在网友的帮助下,我得出了下面的看法:
首先,==运算符两边的两个操作数都是值类型,所以==运算符只比较其值是否相等即可。
然后,也是最重要的一点就是,==运算符两边的操作数的类型必须要一致,并且这个类型要重载了==运算符的实现。很显然,我自定义的Digit结构并没有重载==的实现。而据我推测,byte类型作为c#的基元类型,是实现了==运算符的。因此,在这里,CLR在执行的时候会将Digit转换到byte类型再比较,而转换的结果比转换前的值要大1,所以两者都是6了(注意转换后d的值还是5,但是==比较的是b和将d转换后的返回值)。结果就是True了。这也可以解释为什么会出现两个“Conversion occured”了。因为进行了两次隐式转换,为变量b赋值时进行了一次,==比较的时候又进行了一次。通过单步执行可以更清楚地看到这个过程。
为了更加深入研究这个例子,并且证明我前面的猜测:==运算符两边的操作数的类型必须要一致,并且这个类型要重载了==运算符的实现。我把代码做了一点小的修改,Main方法替换为下面的代码:
Digit d = new Digit(5);
Digit d2 = new Digit(6);
Console.WriteLine(d == d2);
Digit d2 = new Digit(6);
Console.WriteLine(d == d2);
Conversion occured
Conversion occured
False
如果我们把Digit定义中到byte的隐式转换注释掉,那么c#编译器就会报如下的错误:
Operator '==' cannot be applied to operands of type 'Digit' and 'Digit'
因为Digit没有重载==运算符,编译器又找不到存在的任何转换(包括隐式的和显式的),所以就会报错。
这个例子告诉我们,如果我们自己定义了结构,而且需要对该结构类型的对象进行==运算,那么我们就要自己定义==运算符,否则结果就可能不是我们预期的那样了:
using System;
using System.Collections;
struct Digit
{
byte value;
public Digit(byte value)
{
if (value > 9) throw new ArgumentException();
this.value = value;
}
public static implicit operator byte(Digit d)
{
Console.WriteLine("Conversion occured");
return (byte)((int)d.value + 1);
}
public static bool operator ==(byte b, Digit d)
{
return b == d.value;
}
public static bool operator !=(byte b, Digit d)
{
return !(b == d);
}
}
class app
{
static void Main()
{
Digit d = new Digit(5);
byte b = d;
Console.WriteLine(b == d);
}
}
using System.Collections;
struct Digit
{
byte value;
public Digit(byte value)
{
if (value > 9) throw new ArgumentException();
this.value = value;
}
public static implicit operator byte(Digit d)
{
Console.WriteLine("Conversion occured");
return (byte)((int)d.value + 1);
}
public static bool operator ==(byte b, Digit d)
{
return b == d.value;
}
public static bool operator !=(byte b, Digit d)
{
return !(b == d);
}
}
class app
{
static void Main()
{
Digit d = new Digit(5);
byte b = d;
Console.WriteLine(b == d);
}
}
Conversion occured
False