c#运算符重载

释放双眼,带上耳机,听听看~!

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#要求所有的运算符重载都声明为publicstatic,必须是类的静态方法,这表示它们与它们的类或结构相关联,而不是与实例相关联


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 + &quot;+++&quot; + 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, &quot;晓菜鸟&quot;);
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, &quot;博客园&quot;);
6            var resultOne = student + 3;
7            var resultTwo = student + &quot;晓菜鸟&quot;;
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 + &quot;-晓菜鸟&quot;);
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, &quot;晓菜鸟&quot;);
7            Student stuB = new Student(2, 21, &quot;博客园&quot;);
8            Student stuC = new Student(1, 23, &quot;攻城狮&quot;);
9            message = stuA.Name + (stuA == stuC ? &quot;是&quot; : &quot;不是&quot;) + stuC.Name + &quot;&lt;br /&gt;&quot;;
10            message += stuA.Name + (stuA != stuB ? &quot;不是&quot; : &quot;是&quot;) + stuB.Name + &quot;&lt;br /&gt;&quot;;
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 &quot;编号:&quot; + Id + &quot;;姓名:&quot; + Name + &quot;;年龄:&quot; + 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        //重载运算符&quot;+&quot;,计算两个学生的年龄总和.
49        public static Student operator +(Student lhs, Student rhs)
50        {
51            return new Student(0, lhs.Age + rhs.Age, lhs.Name + &quot; 和 &quot; + rhs.Name);
52        }
53
54        //重载运算符&quot;-&quot;,计算两个学生的年龄差.
55        public static Student operator -(Student lhs, Student rhs)
56        {
57            return new Student(0, Math.Abs(lhs.Age - rhs.Age), lhs.Name + &quot; 和 &quot; + 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, &quot;晓菜鸟&quot;);
5            Student stuB = new Student(2, 21, &quot;博客园&quot;);
6            Student stuC = new Student(1, 23, &quot;攻城狮&quot;);
7            message = stuA.Name + (stuA == stuC ? &quot;是&quot; : &quot;不是&quot;) + stuC.Name + &quot;&lt;br /&gt;&quot;;
8            message += stuA.Name + (stuA != stuB ? &quot;不是&quot; : &quot;是&quot;) + stuB.Name + &quot;&lt;br /&gt;&quot;;
9            Student stuSum = stuA + stuC;
10            Student stuDiffe = stuA - stuB;
11            message += stuSum.Name + &quot;的年龄总和为:&quot; + stuSum.Age + &quot;&lt;br /&gt;&quot;;
12            message += stuDiffe.Name + &quot;的年龄差为:&quot; + stuDiffe.Age + &quot;&lt;br /&gt;&quot;;
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        //重载运算符&quot;+&quot;,计算两个学生的年龄总和.
36        public static Student operator +(Student lhs, Student rhs)
37        {
38            return new Student(0, lhs.Age + rhs.Age, lhs.Name + &quot; 和 &quot; + rhs.Name);
39        }
40
41        //重载运算符&quot;-&quot;,计算两个学生的年龄差.
42        public static Student operator -(Student lhs, Student rhs)
43        {
44            return new Student(0, Math.Abs(lhs.Age - rhs.Age), lhs.Name + &quot; 和 &quot; + 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 &quot;编号:&quot; + Id + &quot;;姓名:&quot; + Name + &quot;;年龄:&quot; + 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, &quot;刘主任&quot;, &quot;教导室主任&quot;);
7            Teacher teaB = new Teacher(12, 45, &quot;吕老师&quot;, &quot;校长助理&quot;);
8            Teacher teaC = new Teacher(11, 27, &quot;刘老师&quot;, &quot;小二班班主任&quot;);
9            Student stuOne = new Student(1, 18, &quot;晓菜鸟&quot;);
10            Student stuTwo = new Student(2, 21, &quot;博客园&quot;);
11            Student stuThree = new Student(1, 23, &quot;攻城狮&quot;);
12            message = stuOne.Name + (stuOne == stuThree ? &quot;是&quot; : &quot;不是&quot;) + stuThree.Name + &quot;&lt;br /&gt;&quot;;
13            message += stuOne.Name + (stuOne != stuTwo ? &quot;不是&quot; : &quot;是&quot;) + stuTwo.Name + &quot;&lt;br /&gt;&quot;;
14            message += string.Format(&quot;{0}和{1}的年龄总和为:{2}&lt;br /&gt;&quot;, stuOne.Name, stuThree.Name, stuOne + stuThree);
15            message += string.Format(&quot;{0}和{1}的年龄差为:{2}&lt;br /&gt;&quot;, 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 &quot;编号:&quot; + Id + &quot;;姓名:&quot; + Name + &quot;;年龄:&quot; + Age;
38        }
39    }
40
41    public class Teacher:People
42    {
43        /// &lt;summary&gt; 职务 &lt;/summary&gt;
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        //重载运算符&quot;+&quot;,计算年龄总和.
65        public static int operator +(People lhs, People rhs)
66        {
67            return lhs.Age + rhs.Age;
68        }
69
70        //重载运算符&quot;-&quot;,计算年龄差.
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

"运算符重载实例三"运行结果图:

c#运算符重载 

 

给TA打赏
共{{data.count}}人
人已打赏
安全技术

详解Node.js API系列 Http模块(1) 构造一个简单的静态页服务器

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索