C#中如何利用操作符重载和转换操作符 (转载)
操作符重载
有的编程语言允许一个类型定义操作符应该如何操作类型的实例,比如string类型和int类型都重载了(==)和(+)等操作符,当编译器发现两个int类型的实例使用+操作符的时候,编译器会生成把两个整数加到一起的代码。
当编译器发现两个string类型的实例使用+操作符的时候,编译器会生成把两个字符串连接到一起的代码。那么编译器怎么就会知道这样做呢?如何进行操作符重载呢?
下面C#代码展示了一个类中如何进行操作符重载:
namespace DoNet.Seven.ConsoleApplicationTest
{
class Program
{
static void Main(string[] args)
{
rational r1 = new rational();
rational r2 = new rational();
rational r3= r1 + r2;
Console.WriteLine(r3.Value);
Console.ReadKey(); }
}
//有理数
public sealed class rational
{
private int _value = ; public int Value
{
get { return _value; }
set { _value = value; }
}
public rational(int value)
{
this._value = value;
} public rational()
{ }
public static rational operator+(rational num1,rational num2)
{
rational result = new rational(num1.Value+num2.Value);
return result;
} }
}
运行代码输入结果是15
用IL工具看下编译器生成的代码如下:

- 首先CLR规范要求操作符重载方法必须是public和static方法。另外,C#编译器要求操作符重载方法至少有一个参数的类型与当前定义的这个方法的类型相同。之所以这样做,是为了是编译器能在合理的时间内找到要绑定的操作符方法。
 - 编程语言的编译器看到源码中出现一个+操作符时,会检查是否有一个操作数的类型定义了一个名为op_Addtion的specialname方法,而且该方法的参数兼容于操作数的类型,如果存在这样的一个方法,编译器就生成调用它的代码。如果不存在这样的一个方法,就生成一个编译错误。
 - 对于其它操作符编译之后对应的方法如下表所示(左边是一元操作符,右边是二元操作符)
 


转换操作符
当设计一个类型时应该考虑到和其它类型之间的转换,这个其实很重要,将对我们的编码有很大的好处,就像每个类型都会有的一个方法Tostring()一样,我们定义一个int类型,可以很方便的用tostring()方法把
int转换为string,当然也可以转换为其它类型。就像上面的rational一样,如果能将一个int或者double转换为一个rational,就会很方便,反之亦然。
//有理数
public sealed class rational
{
private int _value = ; public int Value
{
get { return _value; }
set { _value = value; }
}
public rational(int value)
{
this._value = value;
} public rational(double value)
{
this._value =(int)value;
}
public rational()
{ } public int ToInt()
{
return _value;
}
public double ToDouble()
{
return (double)_value;
}
public static rational operator+(rational num1,rational num2)
{
rational result = new rational(num1.Value+num2.Value);
return result;
} }
1、调用这些构造器和方法,开发人员可以很方便的将int和double对象转换成rational对象,这将给编程工作带来很多方便。设计类型时,应该认真考虑类型需要支持的转换构造器和方法。
2、int i=10;long j=i;这样的代码我们经常会看到,那么从int类型到long类型的转换为什么就可以隐士的进行呢?这就涉及到了我们的转换操作符,下面我们也为rational定义几个转换操作符。
namespace DoNet.Seven.ConsoleApplicationTest
{
class Program
{
static void Main(string[] args)
{
int n = ;
rational r1 = n;
double d=(double)r1; Console.WriteLine(r1.Value);
Console.WriteLine(d.ToString());
Console.ReadKey(); }
}
//有理数
public sealed class rational
{
private int _value = ; public int Value
{
get { return _value; }
set { _value = value; }
}
public rational(int value)
{
this._value = value;
} public rational(double value)
{
this._value =(int)value;
}
public rational()
{ } public int ToInt()
{
return _value;
}
public double ToDouble()
{
return (double)_value;
}
public static rational operator+(rational num1,rational num2)
{
rational result = new rational(num1.Value+num2.Value);
return result;
} public static implicit operator rational(int value)
{
return new rational(value);
}
public static implicit operator rational(double value)
{
return new rational(value);
}
public static explicit operator int(rational value)
{
return value.ToInt();
}
public static explicit operator double(rational value)
{
return value.ToDouble();
} }
}
输出的结果是10、10。 我们可以在rational、int、double之间来回转换,是不是觉得挺方便的,在这个过程中,编译器又帮我们做了什么呢?
在C#中,implicit关键字告诉编译器为了生成代码来调用方法,不需要在源代码中进行显示转换(即隐式转换),相反,explicit关键字告诉编译器只有在发现了显示转型时,才调用方法。
在implicit或explicit关键字之后,要指定operator关键字告诉编译器该方法是一个转换操作符。在operator之后,指定对象要转换成什么类型。在圆括号内,则指定要从什么类型转换。
C#编译器检测到代码中的转型,并内部生成IL代码来调用rational类型定义的转换操作符方法,如果用反编译器看的话可以发现,转换操作符方法会生成下面这样的代码:

结论
不论是操作符重载还是转换操作符,都是在设计类型是考虑到我们编码方便而设计的,下面我们看下C#中decimal类型中的定义。

隐式转换重载可自动作用于其它运算符重载
C#的隐式转换重载很多时候会自动作用于其它运算符重载,下面例子中我们在Program类的Main方法中,使用rational类型的对象rational,来加上int类型的变量number:
//有理数
public class rational
{
private int _value = ; public int Value
{
get { return _value; }
set { _value = value; }
}
public rational(int value)
{
this._value = value;
} public rational()
{ } public static rational operator +(rational num1, rational num2)
{
Console.WriteLine("rational operator + invoked!"); rational result = new rational(num1.Value + num2.Value);
return result;
} public static implicit operator rational(int value)
{
Console.WriteLine("rational implicit operator invoked!"); return new rational(value);
}
} class Program
{
static void Main(string[] args)
{
int number = ;
rational rational = new rational();
rational rationalAdd = rational + number;//rational类型加上int类型 Console.WriteLine("Press any key to quit...");
Console.ReadKey();
}
}
结果如下:

从输出结果,我们可以看到由于rational类型的对象rational加上int类型的变量number,C#首先调用了rational类的implicit operator隐式转换重载方法,将int类型隐式转换为了rational类型,然后才调用rational类的operator +操作符重载方法,来做的加法。
C#会自动选择最优的操作符重载方法
此外,C#会自动调用最优的操作符重载方法,例如下例中我们有两个类rationalA和rationalB,我们使用rationalA类的变量rationalX,加上rationalB类的变量rationalY,代码如下:
public class rationalA
{
private int _value = ; public int Value
{
get { return _value; }
set { _value = value; }
}
public rationalA(int value)
{
this._value = value;
} public rationalA()
{ } public static rationalA operator +(rationalA num1, rationalA num2)
{
Console.WriteLine("rationalA operator rationalA + rationalA invoked!"); rationalA result = new rationalA(num1.Value + num2.Value);
return result;
} public static rationalA operator +(rationalA num1, rationalB num2)
{
Console.WriteLine("rationalA operator rationalA + rationalB invoked!"); rationalA result = new rationalA(num1.Value + num2.Value);
return result;
} public static implicit operator rationalA(rationalB value)
{
Console.WriteLine("rationalA implicit operator invoked!"); return new rationalA(value.Value);
}
} public class rationalB
{
private int _value = ; public int Value
{
get { return _value; }
set { _value = value; }
}
public rationalB(int value)
{
this._value = value;
} public rationalB()
{ }
} class Program
{
static void Main(string[] args)
{
rationalA rationalX = new rationalA();
rationalB rationalY = new rationalB(); rationalA rationalZ = rationalX + rationalY;//rationalA类型加上rationalB类型 Console.WriteLine("Press any key to quit...");
Console.ReadKey();
}
}
结果如下:

可以看到当我们在调用rationalA类型加上rationalB类型时:
C#并没有先调用rationalA类的implicit operator重载方法将rationalB转换为rationalA,再调用rationalA类的operator +(rationalA num1, rationalA num2)重载方法做加法。
而是直接调用了rationalA类的operator +(rationalA num1, rationalB num2)重载方法,说明C#觉得这才是最优的运算符重载方法。
如果我们注释掉rationalA类的operator +(rationalA num1, rationalB num2)重载方法,运行结果就会如下:

操作符重载是有顺序的
需要注意C#在重载操作符时,是有先后顺序的,例如下面的rationalA类中我们定义了操作符重载方法operator +(rationalA num1, rationalB num2),但是在Program类的Main方法中,我们反过来用rationalB类型加上rationalA类型:
public class rationalA
{
private int _value = ; public int Value
{
get { return _value; }
set { _value = value; }
}
public rationalA(int value)
{
this._value = value;
} public rationalA()
{ } public static rationalA operator +(rationalA num1, rationalB num2)
{
Console.WriteLine("rationalA operator rationalA + rationalB invoked!"); rationalA result = new rationalA(num1.Value + num2.Value);
return result;
} public static implicit operator rationalA(rationalB value)
{
Console.WriteLine("rationalA implicit operator invoked!"); return new rationalA(value.Value);
}
} public class rationalB
{
private int _value = ; public int Value
{
get { return _value; }
set { _value = value; }
}
public rationalB(int value)
{
this._value = value;
} public rationalB()
{ }
} class Program
{
static void Main(string[] args)
{
rationalA rationalX = new rationalA();
rationalB rationalY = new rationalB(); rationalA rationalZ = rationalY + rationalX;//rationalB类型加上rationalA类型会报错,因为rationalA类没有定义操作符重载方法operator +(rationalB num1, rationalA num2) Console.WriteLine("Press any key to quit...");
Console.ReadKey();
}
}
这样C#会报编译错误,因为rationalB类型加上rationalA类型,需要定义操作符重载方法operator +(rationalB num1, rationalA num2),但是我们只在rationalA类中定义了操作符重载方法operator +(rationalA num1, rationalB num2):

不要定义有二义性的操作符重载方法
C#遇到具有二义性的操作符重载方法时会报错,如下所示:
public class rationalA
{
private int _value = ; public int Value
{
get { return _value; }
set { _value = value; }
}
public rationalA(int value)
{
this._value = value;
} public rationalA()
{ } //rationalA类定义了operator +(rationalA num1, rationalB num2)操作符重载方法
public static rationalA operator +(rationalA num1, rationalB num2)
{
Console.WriteLine("rationalA operator rationalA + rationalB invoked!"); rationalA result = new rationalA(num1.Value + num2.Value);
return result;
} public static implicit operator rationalA(rationalB value)
{
Console.WriteLine("rationalA implicit operator invoked!"); return new rationalA(value.Value);
}
} public class rationalB
{
private int _value = ; public int Value
{
get { return _value; }
set { _value = value; }
}
public rationalB(int value)
{
this._value = value;
} public rationalB()
{ } //rationalB类也定义了operator +(rationalA num1, rationalB num2)操作符重载方法
public static rationalB operator +(rationalA num1, rationalB num2)
{
Console.WriteLine("rationalA operator rationalA + rationalB invoked!"); rationalB result = new rationalB(num1.Value + num2.Value);
return result;
}
} class Program
{
static void Main(string[] args)
{
rationalA rationalX = new rationalA();
rationalB rationalY = new rationalB(); var rationalZ = rationalX + rationalY;//rationalA类型加上rationalB类型会报错,因为rationalA类定义了操作符重载方法operator +(rationalA num1, rationalB num2),而rationalB类也定义了同样的操作符重载方法operator +(rationalA num1, rationalB num2) Console.WriteLine("Press any key to quit...");
Console.ReadKey();
}
}
上面的代码编译时,C#会报错,错误如下:

可以看到由于我们既在rationalA类中定义了操作符重载方法operator +(rationalA num1, rationalB num2),又在rationalB类中也定义了同样的操作符重载方法operator +(rationalA num1, rationalB num2),当使用rationalA类型加上rationalB类型时,C#就不知道该调用哪一个重载方法了,产生了二义性,所以切记操作符重载不能滥用,否则会带来不必要的麻烦。
C#中如何利用操作符重载和转换操作符 (转载)的更多相关文章
- C#中如何利用操作符重载和转换操作符
		
操作符重载 有的编程语言允许一个类型定义操作符应该如何操作类型的实例,比如string类型和int类型都重载了(==)和(+)等操作符,当编译器发现两个int类型的实例使用+操作符的时候,编译器会生成 ...
 - C#关于操作符重载与转换
		
随便写写 首先,假设我们有一个Person类型 其类型定义如下 class Person { public string Name { get; set; } = "Person" ...
 - (二) operator、explicit与implicit 操作符重载
		
有的编程语言允许一个类型定义操作符应该如何操作类型的实例,比如string类型和int类型都重载了(==)和(+)等操作符,当编译器发现两个int类型的实例使用+操作符的时候,编译器会生成把两个整 ...
 - [置顶]        operator overloading(操作符重载,运算符重载)运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy)
		
operator overloading(操作符重载,运算符重载) 所谓重载就是重新赋予新的意义,之前我们已经学过函数重载,函数重载的要求是函数名相同,函数的参数列表不同(个数或者参数类型).操作符重 ...
 - C++解析(17):操作符重载
		
0.目录 1.操作符重载 2.完善的复数类 3.小结 1.操作符重载 下面的复数解决方案是否可行? 示例1--原有的解决方案: #include <stdio.h> class Compl ...
 - c++ 操作符重载和友元
		
操作符重载(operator overloading)是C++中的一种多态,C++允许用户自定义函数名称相同但参数列表不同的函数,这被称为函数重载或函数多态.操作符重载函数的格式一般为: operat ...
 - C++一些注意点之操作符重载
		
重载操作符需要注意 (1)重载操作符必须具有一个类类型操作数.不能重载内建类型的操作符. operator +(int,int);//这个是错误的,都为内建类型 operator +(int,clas ...
 - 15.C++-操作符重载
		
首先回忆下以前学的函数重载 函数重载 函数重载的本质为相互独立的不同函数 通过函数名和函数参数来确定函数调用 无法直接通过函数名得到重载函数的入口地址 函数重载必然发生在同一个作用域中 类中的函数重载 ...
 - 15.C++-操作符重载、并实现复数类
		
首先回忆下以前学的函数重载 函数重载 函数重载的本质为相互独立的不同函数 通过函数名和函数参数来确定函数调用 无法直接通过函数名得到重载函数的入口地址 函数重载必然发生在同一个作用域中 类中的函数重载 ...
 
随机推荐
- 循环遍历正则验证input框内容合法性
			
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
 - 修改Nginx 伪静态Rewrite规则 安装Chevereto
			
Chevereto 是目前最为强大的 PHP 图床系统,通过它可部署多用户公开或私有的图片存储服务,现在 Chevereto 出了免费的版本,小伙伴可以围观一下. https://github.com ...
 - <Android 应用 之路> MPAndroidChart~BubbleChart(气泡图) and RadarChart(雷达图)
			
简介 MPAndroidChart是PhilJay大神给Android开发者带来的福利.MPAndroidChart是一个功能强大并且使用灵活的图表开源库,支持Android和iOS两种,这里我们暂时 ...
 - SpringBoot整合mybatis——配置mybatis驼峰命名规则自动转换
			
一.简述 mybatis驼峰式命名规则自动转换: 使用前提:数据库表设计按照规范“字段名中各单词使用下划线"_"划分”: 使用好处:省去mapper.xml文件中繁琐编写表字段列表 ...
 - Wasserstein GAN
			
在GAN的相关研究如火如荼甚至可以说是泛滥的今天,一篇新鲜出炉的arXiv论文<Wasserstein GAN>却在Reddit的Machine Learning频道火了,连Goodfel ...
 - 微信小程序-02-项目文件之间配合和调用关系
			
微信小程序-02-项目文件之间配合和调用关系 我就不说那么多了,我是从官方文档拷贝的,然后加上一些自己的笔记,不喜勿喷 官方文档:https://developers.weixin.qq.com/mi ...
 - volley2--volley的使用和架构
			
图片: 下面只是笼统的介绍,大家可以对比自己的想法,看看自己是不是有什么考虑不周的(如果是你实现这样一个框架的话) 1,Request的设计,我们在得到response之后,我们可能根据项目需求希望有 ...
 - PyCharm添加Selenium与Appium依赖
 - 详细故障排除步骤:针对 Azure 中到 Windows VM 的远程桌面连接问题
			
本文提供详细的故障排除步骤,用于为基于 Windows 的 Azure 虚拟机诊断和修复复杂的远程桌面错误. Important 若要消除更常见的远程桌面错误,请务必先阅读远程桌面的基本故障排除文章, ...
 - SQL Server ->> 性能调优案例之 -- 包含递归查询的视图导致整个查询语句性能下降
			
有个语句最近性能下降很厉害,原本1秒就可以查询完毕的事情现在居然需要3-4分钟. 首先我的做法是先快速找出导致整个语句下降的元凶.在这个例子里面查询语句有3个JOIN字句,我通过删除某一个JOIN节点 ...