C#最常见的重载是构造函数重载,各种方法包括ToString()也可以重载,运算符+-*/也可以重载,今天我们就来说说运算符重载。
一、简介
C# 允许用户定义的类型通过使用 operator 关键字定义静态成员函数来重载运算符。注意必须用public修饰且必须是类的静态的方法。但并非所有内置运算符都可以被重载,详见表1:
| 运算符 | 可重载性 |
| +、-、!、~、++、–、true、false | 可以重载这些一元运算符, true和false运算符必须成对重载 |
| +、-、*、/、%、&、|、^、<<、>> | 可以重载这些二元运算符 |
| ==、!=、<、>、<=、>= | 可以重载比较运算符,必须成对重载 |
| &&、|| | 不能重载条件逻辑运算符,但可以使用能够重载的&和|进行计算 |
| [] | 不能重载数组索引运算符,但可以定义索引器 |
| () | 不能重载转换运算符,但可以定义新的转换运算符(请参见 explicit 和 implicit) |
| +=、-=、*=、/=、%=、&=、|=、^=、<<=、>>= | 不能显式重载赋值运算符,在重写单个运算符如+、-、%时,它们会被 隐式重写 |
| =、.、?:、->、new、is、sizeof、typeof | 不能重载这些运算符 |
1 | 1 |
表1
二、声明
operator 关键字用于在类或结构声明中声明运算符。运算符声明可以采用下列四种形式之一:
-
public static result-type operator unary-operator ( op-type operand )
-
public static result-type operator binary-operator ( op-type operand, op-type2 operand2 )
-
public static implicit operator conv-type-out ( conv-type-in operand )
-
public static explicit operator conv-type-out ( conv-type-in operand )
参数说明:
result-type:运算符的结果类型。
unary-operator:下列运算符之一:+ – ! ~ ++ — true false
op-type:第一个(或唯一一个)参数的类型。
operand:第一个(或唯一一个)参数的名称。
binary-operator:其中一个:+ – * / % & | ^ << >> == != > < >= <=
op-type2:第二个参数的类型。
operand2:第二个参数的名称。
conv-type-out:类型转换运算符的目标类型。
conv-type-in:类型转换运算符的输入类型。
注意:
1、运算符重载的声明方式:operator 关键字告诉编译器,它实际上是一个运算符重载,后面是相关运算符的符号。
2、运算符只能采用值参数,不能采用ref或out参数。可参考注意事项一实例。
3、前两种形式声明了用户定义的重载内置运算符的运算符。op-type 和 op-type2 中至少有一个必须是封闭类型(即运算符所属的类型,或理解为自定义的类型)。例如,这将防止重定义整数加法运算符。可参考注意事项二实例。
4、后两种形式声明了转换运算符。conv-type-in 和 conv-type-out 中正好有一个必须是封闭类型(即转换运算符只能从它的封闭类型转换为其他某个类型,或从其他某个类型转换为它的封闭类型)。
5、对于二元运算符,第一个参数是放在运算符左边的值,一般命名为lhs;第二个参数是放在运算符右边的值,一般命名为rhs。
6、C#要求所有的运算符重载都声明为public和static,必须是类的静态方法,这表示它们与它们的类或结构相关联,而不是与实例相关联。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 1public class Student
2 {
3 public int Age { get; set; }
4 public string Name { get; set; }
5
6 public Student()
7 { }
8
9 public Student(int age, string name)
10 {
11 this.Age = age;
12 this.Name = name;
13 }
14
15 //语法错误:ref和out参数在此上下文中无效(去掉ref和out关键字即可).
16 public static Student operator +(ref Student stu1,out Student stu2)
17 {
18 return new Student(stu1.Age + stu2.Age, stu1.Name + "+++" + stu2.Name);
19 }
20 }
21
22注意事项一实例
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 1public class Student
2 {
3 public int Age { get; set; }
4 public string Name { get; set; }
5
6 public Student()
7 { }
8
9 public Student(int age, string name)
10 {
11 this.Age = age;
12 this.Name = name;
13 }
14
15 //编译错误:二元运算符的参数之一必须是包含类型(参数c1、c2中有一个类型为Student即可).
16 public static Student operator +(int c1, int c2)
17 {
18 return new Student(c1 + c2, "晓菜鸟");
19 }
20 }
21
22注意事项二实例
23
比较运算符的重载:
a、C#要求成对重载比较运算符,如果重载了==,也必须重载!=,否则会产生编译错误。
b、比较运算符必须返回bool类型的值,这是与其他算术运算符的根本区别。
c、在重载==和!=时,还应该重载从System.Object中继承的Equals()和GetHashCode()方法,否则会产生一个编译警告,原因是Equals方法应执行与==运算符相同的相等逻辑。
d、C# 不允许重载**=运算符,但如果重载例如+**运算符,编译器会自动使用+运算符的重载来执行+=运算符的操作。
e、任何运算符声明的前面都可以有一个可选的属性(C# 编程指南)列表。
重点:
运算符重载其实就是函数重载。首先通过指定的运算表达式调用对应的运算符函数,然后再将运算对象转化为运算符函数的实参,接着根据实参的类型来确定需要调用的函数的重载,这个过程是由编译器完成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 1public class UserController : Controller
2 {
3 public ActionResult Index()
4 {
5 Student student = new Student(18, "博客园");
6 var resultOne = student + 3;
7 var resultTwo = student + "晓菜鸟";
8 return View();
9 }
10 }
11
12 public class Student
13 {
14 public int Age { get; set; }
15 public string Name { get; set; }
16
17 public Student()
18 { }
19
20 public Student(int age, string name)
21 {
22 this.Age = age;
23 this.Name = name;
24 }
25
26 public static Student operator +(Student stu, int c2)
27 {
28 return new Student(stu.Age + c2, stu.Name + "-晓菜鸟");
29 }
30
31 public static Student operator +(Student stu, string suffix)
32 {
33 return new Student(stu.Age + 11, stu.Name + suffix);
34 }
35 }
36
37参考实例
38
三、实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 1public class UserController : Controller
2 {
3 public ActionResult Index()
4 {
5 string message = string.Empty;
6 Student stuA = new Student(1, 18, "晓菜鸟");
7 Student stuB = new Student(2, 21, "博客园");
8 Student stuC = new Student(1, 23, "攻城狮");
9 message = stuA.Name + (stuA == stuC ? "是" : "不是") + stuC.Name + "<br />";
10 message += stuA.Name + (stuA != stuB ? "不是" : "是") + stuB.Name + "<br />";
11 message += stuA;
12 ViewData.Model = message;
13 return View();
14 }
15 }
16
17 public class Student
18 {
19 public int Id { get; set; }
20 public int Age { get; set; }
21 public string Name { get; set; }
22
23 public Student()
24 { }
25
26 public Student(int id,int age, string name)
27 {
28 this.Id = id;
29 this.Age = age;
30 this.Name = name;
31 }
32
33 //重载ToString(),自定义格式化输出.
34 public override string ToString()
35 {
36 return "编号:" + Id + ";姓名:" + Name + ";年龄:" + Age;
37 }
38 }
39
40 public class Teacher
41 {
42 public int Id { get; set; }
43
44 public string Name { get; set; }
45
46 public int Duties { get; set; }
47
48 //重载运算符"+",计算两个学生的年龄总和.
49 public static Student operator +(Student lhs, Student rhs)
50 {
51 return new Student(0, lhs.Age + rhs.Age, lhs.Name + " 和 " + rhs.Name);
52 }
53
54 //重载运算符"-",计算两个学生的年龄差.
55 public static Student operator -(Student lhs, Student rhs)
56 {
57 return new Student(0, Math.Abs(lhs.Age - rhs.Age), lhs.Name + " 和 " + rhs.Name);
58 }
59
60 //重载==运算符,同一Id的学生默认为同一个人.
61 public static bool operator ==(Student lhs, Student rhs)
62 {
63 return lhs.Id == rhs.Id;
64 }
65
66 //比较运算符必须成对重载.
67 public static bool operator !=(Student lhs, Student rhs)
68 {
69 return !(lhs == rhs);
70 }
71 }
72
73
74
75
编译"运算符重载实例一"将产生错误,错误信息:二元运算符的参数之一必须是包含类型。这里的错误跟我们上面的"注意事项二实例"的错误大同小异,因为在Teacher类中,他不知道Student是什么,只有Student自己知道。只有Student才能决定自己能不能"+-",而不能让别人决定。operator + 相当于一个函数,我们可以这样去理解,operator +(op-type operand, op-type2 operand2) 等于 op-type.operator +(operand,operand2) 或者 op-type2.operator +(operand,operand2)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 1public ActionResult Index()
2 {
3 string message = string.Empty;
4 Student stuA = new Student(1, 18, "晓菜鸟");
5 Student stuB = new Student(2, 21, "博客园");
6 Student stuC = new Student(1, 23, "攻城狮");
7 message = stuA.Name + (stuA == stuC ? "是" : "不是") + stuC.Name + "<br />";
8 message += stuA.Name + (stuA != stuB ? "不是" : "是") + stuB.Name + "<br />";
9 Student stuSum = stuA + stuC;
10 Student stuDiffe = stuA - stuB;
11 message += stuSum.Name + "的年龄总和为:" + stuSum.Age + "<br />";
12 message += stuDiffe.Name + "的年龄差为:" + stuDiffe.Age + "<br />";
13 message += stuA;
14 ViewData.Model = message;
15 return View();
16 }
17 }
18
19 public class Student
20 {
21 public int Id { get; set; }
22 public int Age { get; set; }
23 public string Name { get; set; }
24
25 public Student()
26 { }
27
28 public Student(int id,int age, string name)
29 {
30 this.Id = id;
31 this.Age = age;
32 this.Name = name;
33 }
34
35 //重载运算符"+",计算两个学生的年龄总和.
36 public static Student operator +(Student lhs, Student rhs)
37 {
38 return new Student(0, lhs.Age + rhs.Age, lhs.Name + " 和 " + rhs.Name);
39 }
40
41 //重载运算符"-",计算两个学生的年龄差.
42 public static Student operator -(Student lhs, Student rhs)
43 {
44 return new Student(0, Math.Abs(lhs.Age - rhs.Age), lhs.Name + " 和 " + rhs.Name);
45 }
46
47 //重载==运算符,同一Id的学生默认为同一个人.
48 public static bool operator ==(Student lhs, Student rhs)
49 {
50 return lhs.Id == rhs.Id;
51 }
52
53 //比较运算符必须成对重载.
54 public static bool operator !=(Student lhs, Student rhs)
55 {
56 return !(lhs == rhs);
57 }
58
59 //重载ToString(),自定义格式化输出.
60 public override string ToString()
61 {
62 return "编号:" + Id + ";姓名:" + Name + ";年龄:" + Age;
63 }
64 }
65
66 public class Teacher
67 {
68 public int Id { get; set; }
69
70 public string Name { get; set; }
71
72 public int Duties { get; set; }
73 }
74
"运算符重载实例二"是完全没有问题的,这个时候我们想一个问题,将如我们的Teacher类也涉及到求教师年龄的总和和差值怎么办?难道只能重写一遍?不知道您有什么好的思路和见解,不妨在评论里面留下您的看法!请多多指教,晓菜鸟不胜感激!
我这里想到的就是继承,让子类去继承父类的重载!请看"运算符重载实例三"。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 1public class UserController : Controller
2 {
3 public ActionResult Index()
4 {
5 string message = string.Empty;
6 Teacher teaA = new Teacher(11, 30, "刘主任", "教导室主任");
7 Teacher teaB = new Teacher(12, 45, "吕老师", "校长助理");
8 Teacher teaC = new Teacher(11, 27, "刘老师", "小二班班主任");
9 Student stuOne = new Student(1, 18, "晓菜鸟");
10 Student stuTwo = new Student(2, 21, "博客园");
11 Student stuThree = new Student(1, 23, "攻城狮");
12 message = stuOne.Name + (stuOne == stuThree ? "是" : "不是") + stuThree.Name + "<br />";
13 message += stuOne.Name + (stuOne != stuTwo ? "不是" : "是") + stuTwo.Name + "<br />";
14 message += string.Format("{0}和{1}的年龄总和为:{2}<br />", stuOne.Name, stuThree.Name, stuOne + stuThree);
15 message += string.Format("{0}和{1}的年龄差为:{2}<br />", stuOne.Name, stuTwo.Name, stuOne - stuTwo);
16 message += stuOne;
17 ViewData.Model = message;
18 return View();
19 }
20 }
21
22 public class Student:People
23 {
24 public Student()
25 { }
26
27 public Student(int id,int age, string name)
28 {
29 this.Id = id;
30 this.Age = age;
31 this.Name = name;
32 }
33
34 //重载ToString(),自定义格式化输出.
35 public override string ToString()
36 {
37 return "编号:" + Id + ";姓名:" + Name + ";年龄:" + Age;
38 }
39 }
40
41 public class Teacher:People
42 {
43 /// <summary> 职务 </summary>
44 public string Duties { get; set; }
45
46 public Teacher() { }
47
48 public Teacher(int id, int age, string name, string duties)
49 {
50 this.Id = id;
51 this.Age = age;
52 this.Name = name;
53 this.Duties = duties;
54 }
55 }
56
57 //abstract:抽象类用做基类,不能被实例化,用途是派生出其他非抽象类.
58 public abstract class People
59 {
60 public int Id { get; set; }
61 public int Age { get; set; }
62 public string Name { get; set; }
63
64 //重载运算符"+",计算年龄总和.
65 public static int operator +(People lhs, People rhs)
66 {
67 return lhs.Age + rhs.Age;
68 }
69
70 //重载运算符"-",计算年龄差.
71 public static int operator -(People lhs, People rhs)
72 {
73 return Math.Abs(lhs.Age - rhs.Age);
74 }
75
76 //重载==运算符,Id相同则视为相等.
77 public static bool operator ==(People lhs, People rhs)
78 {
79 return lhs.Id == rhs.Id;
80 }
81
82 //比较运算符必须成对重载.
83 public static bool operator !=(People lhs, People rhs)
84 {
85 return !(lhs == rhs);
86 }
87 }
88
89
90
91
"运算符重载实例三"运行结果图: