《运算符重载和自定义类型变换.ppt》由会员分享,可在线阅读,更多相关《运算符重载和自定义类型变换.ppt(30页珍藏版)》请在金锄头文库上搜索。
1、运算符重载运算符重载运算符重载运算符重载n运算符重载允许为运算指定用户定义的运算符实现,其中一个或两个操作数是用户定义的类或结构类型 n所有一元和二元运算符都具有可自动用于任何表达式的预定义实现,可通过在类或结构中设置 operator 声明来引入用户定义的实现,其优先级总是高于预定义运算符实现n重载运算符便于程序员编写同时含有ADT和原生基本类型的中缀表达式,使程序更短,结构更清晰复数类的四则运算复数类的四则运算n定义一个复数类Complex,将其多个实例进行四则运算,如何编程实现?static void Main() Complex c1 = new Complex(1, 2); Comp
2、lex c2 = new Complex(3.5, -2); Complex c3 = new Complex(-4, 23); /result=c1+c2-c3如何得到? Complex result = c1.Add(c2).Sub(c3); /将Add、Sub改成静态方法会不会更好些? Complex result2 = Complex.Sub(Complex.Add(c1, c2), c3); Console.WriteLine(result2.Print(); Console.Read(); 在实现了需要的功能后,有没有一种让用户用更习惯、可读性、可编程性更好的方法来进行类似的运算呢
3、?解决方案:运算符重载!可重载的运算符可重载的运算符n一元运算符+ - ! + - true falsen二元运算符+ - * / % & | = != = = public class Complex public int real; public int imaginary;public Complex() public Complex(int real, int imaginary) this.real = real; this.imaginary = imaginary; public static Complex operator +(Complex c1, Complex c2)
4、return new Complex(c1.real+c2.real, c1.imaginary +c2.imaginary); public static Complex operator -(Complex c1, Complex c2) return new Complex(c1.real-c2.real, c1.imaginary -c2.imaginary); public override string ToString() return(String.Format(0 + 1i, real, imaginary); 二二元元运运算算符符重重载载示示例例注意函数原形注意函数原形!
5、public static void Main() Complex num1 = new Complex(3, 4); Complex num2 = new Complex(3, 4); Complex num3 = new Complex(-3, 4); Complex sum = num1.Add(num2); Complex result = num1 + num2 - num3; Console.WriteLine(First complex number: 0, num1); Console.WriteLine(Second complex number: 0, num2); Con
6、sole.WriteLine(The sum of the two numbers: 0, sum); Console.WriteLine(The result: 0, result); Console.WriteLine(num1=num2); Console.Read(); 自动调用自动调用num1num1的的ToString()ToString()默认=是比较两个对象的引用是否相同 public static bool operator =(Complex c1, Complex c2) if (c1.real = c2.real & c1.imaginary = c2.imaginar
7、y) return true; else return false; public static bool operator !=(Complex c1, Complex c2) if (c1.real = c2.real & c1.imaginary = c2.imaginary) return false; else return true; 对对=重载一定重载一定要对要对!=!=也进行也进行重载,否则编重载,否则编译器报错译器报错重写重写Equals和和GetHashCode方法方法n警告1:“ConsoleApplication3.Complex”定义运算符 = 或运算符 !=,但不重
8、写 Object.Equals(object o)和GetHashCode方法 n对于引用类型来说,Object.Equals方法默认比较的是对象的相等性。当引用指向同一个对象的时候,它们是相等的,具有相同状态但指向不同对象的引用是不相等的,可对其进行改写,比较其内容是否相同 n对于值类型来说,Equals已经重写过以进行值的比较 GetHashCoden重载Equals必须重载GetHashCoden这是一条编译器的rule。如果你重载了Equals不重载GetHashCode,编译器会发出警报nGetHashCode需要返回一个Int32值,这个值的规律很简单:n如果两个对象相同,则对象返
9、回的HashCode必须相同。n如果两个对象返回的HashCode不相同,则这两个对象必定不相同。n两个对象不相同,不能保证hashCode不同n这样就使得Equals的效率提高很多。因为GetHashCode通常成本比Equals小得多。 public static bool operator =(Complex c1, Complex c2) if (c1.real = c2.real & c1.imaginary = c2.imaginary) return true; else return false; public static bool operator !=(Complex c
10、1, Complex c2) if (c1.real = c2.real & c1.imaginary = c2.imaginary) return false; else return true; public override bool Equals(object obj) Complex temp = obj as Complex; if (this = temp) return true; return false; public override int GetHashCode()/返回返回当前对象的哈希代码当前对象的哈希代码 ,不唯一! return this.ToString()
11、.GetHashCode(); 整理一下,这样更好整理一下,这样更好 public override bool Equals(object obj) if(!(obj is Complex) return false; return this=(Complex)obj; public static bool operator =(Complex c1, Complex c2) return c1.Equals(c2); public static bool operator !=(Complex c1, Complex c2) return !c1.Equals(c2); public ove
12、rride int GetHashCode() return this.ToString().GetHashCode(); 思考思考n如何实现复数的其他运算?加/减乘/除取反显示自定义类型转换自定义类型转换n现在,如果要实现int+Complex怎么办呢?n还有,complex+intComplex+doubledouble+complexfloat+ComplexComplex+float。n为每种运算定义重载太麻烦了,有更好的办法自定义类型转换用户自定义变换用户自定义变换l在不希望进行隐式类型变换或者表达式不合法时,有必要对表达式进行用户自定义的类型变换。lC#支持将用户自定义的ADT和内
13、建的数据类型进行集成lC#使用关键字implicit和explicit来修改单参数的静态方法用户自定义的隐式变换用户自定义的隐式变换nImplicit关键字 用于声明用户定义的隐式类型转换运算符,可通过消除不必要的类型转换来提高源代码的可读 class MyType public static implicit operator int(MyType m) / code to convert from MyType to int MyType x;int i = x; / implicitly call MyTypes MyType-to-int conversion operator用户自定
14、义的显式变换用户自定义的显式变换nexplicit 关键字 用于声明用户定义的显式类型转换运算符class MyType public static explicit operator MyType(int i) / code to convert from int to MyType int i;MyType x = (MyType)i; / int-to-MyType requires cast为为Complex类添加自定义类型转换类添加自定义类型转换Complex num1 = new Complex(2, 3);Complex num2 = new Complex(3, 4);Comp
15、lex num3 = 4.5;Complex sum = num1 + 3 + num2;Console.WriteLine(The sum of the 3 numbers: 0, sum); / / 从实数到复数的自定义隐式转换从实数到复数的自定义隐式转换 / / 复数的实部复数的实部 / public static implicit operator Complex(double r) return new Complex(r, 0);两次隐式转换:1)int-double2)double-Compleximplicit改成改成explicit将会如何将会如何?显式自定义类型转换显式自定
16、义类型转换public static explicit operator Complex(double r) return new Complex(r, 0);public static void Main() Complex num1 = new Complex(2, 3);Complex num2 = new Complex(3, 4);Complex num3 = (Complex)4.5;Complex sum = num1 + (Complex)3 + num2;Console.WriteLine(The sum of the 3 numbers: 0, sum);更多运算符重载的例
17、子。更多运算符重载的例子。 public class DoubOp public DoubOp(int i,int j)x=i;y=j;public static DoubOp operator +(DoubOp a)a.x+;a.y+;return a;public static DoubOp operator -(DoubOp a)a.x-;a.y-;return a;public void Print()Console.WriteLine(this.x+ +this.y);int x,y; class Testpublic static void Main()DoubOp a=new D
18、oubOp(10,29);a.Print();a+;a.Print();a-;a.Print();一一元元运运算算符符重重载载示示例例书书中中重重载载示示例:例:分分数数 class Fraction int numerator; int denominator; public Fraction(int numerator, int denominator) this.numerator = numerator; this.denominator = denominator; public static Fraction operator +(Fraction lhs, Fraction rh
19、s) if (lhs.denominator = rhs.denominator)return new Fraction(lhs.numerator + rhs.numerator, lhs.denominator); else int firstProduct = lhs.numerator * rhs.denominator; int secondProduct = rhs.numerator * lhs.denominator; int newDenominator = lhs.denominator * rhs.denominator; return new Fraction(firs
20、tProduct + secondProduct, newDenominator).Simply(); public override string ToString() return string.Format(0/1, this.numerator, this.denominator); / 对分数进行约简 public Fraction Simply() int num = Math.Abs(this.denominator); /从大到小查找分子分母的最大公约数 while (num 1) if (numerator % num = 0 & denominator % num = 0)
21、 break; else num-; /可约简 if (num 1) Fraction result = new Fraction(numerator / num, denominator / num); return result; return this; public static bool operator =(Fraction lhs, Fraction rhs) Fraction f1 = lhs.Simply(); Fraction f2 = rhs.Simply(); if (f1.numerator = f2.numerator & f1.denominator = f2.d
22、enominator) return true; else return false; /更好的做法:交叉相乘若想等就相同 进一步:怎么比较分数的大小进一步:怎么比较分数的大小n思路n方法一:先进行通分,在分母相同的情况下,由分子比较来得到分数的大小n方法二:将2个分数都转换成double,然后比较double值n注意n重载了重载了必须同时重载必须同时重载=必须同时重载必须同时重载double并比较并比较public static implicit operator double(Fraction f) return (double)(f.numerator) / f.denominator;
23、 public static bool operator (Fraction lhs, Fraction rhs) double l = lhs; double r = rhs; return l r; /测试代码double r = f3 + 3;Console.WriteLine(0+3=1, f3, r);Console.WriteLine(0 is greater than 1 is 2 , f1, f2, f1 f2);编译器还会要求你添加3结果是什么?中间如何转换n问题二:如何实现下列这些操作?ndouble+Fraction-Fractionn Fraction+double -
24、Fraction显式类型变换显式类型变换/测试代码Fraction f5 = (Fraction)3.5 + f4;Console.WriteLine(3.5+0=1, f4, f5); public static explicit operator Fraction(double d) Fraction f = new Fraction(int)(d * 100), 100).Simply(); return f;怎么判断应该用怎么判断应该用显式变换还是隐显式变换还是隐式变换?式变换?更好的做法更好的做法n这里其实double-Fraction是无损的,可implicitnFraction-
25、double是有损的,应为Explicit运算符重载注意点运算符重载注意点n不能被重载的运算符:成员访问、方法调用或 =、&、|、?:、checked、unchecked、new、typeof、as 和 is 运算符n当重载一个二元运算符时,也会隐式重载相应的赋值运算符(若有 )n强制转换运算(如 (T)x)通过提供用户定义的转换来重载。 n一元或二元运算符重载只能作为静态方法实现n应尽可能保持运算符含义的一致性重载方法的签名匹配重载方法的签名匹配n对方法调用和方法声明中的参数列表进行匹配,由编译器来完成n签名匹配算法:1.如果能找到,使用精确匹配2.尝试标准类型提升3.尝试用户自定义转换练习题练习题n为复数类添加乘/除运算符重载n定义一个三维空间坐标类,包含三个分量的值(x,y,z),如何对两个坐标点进行加、减运算,并根据这两个点到原点的距离大小来对这两个点进行大小比较n定义一个角度类,能表示度、分、秒的信息,如100度30分29秒表示一个钝角,为其定义运算符重载,使得能对2个角度进行加、减运算,并能进行大小比较