第十二,十三,十四天:
(伟大的C#仍在继续中)
7.4 比较对象的相等性
7.4.1比较引用对象的相等性
System.Object定义了3个不同的方法,来比较对象的相等性,:ReferenceEquals()和两个版本的Equals(),再加上运算符(==),实际上4种。
1 ReferenceEquals()方法
ReferenceEquals()是一个静态方法,测试两个引用是否引用类的同一个实例,特别是两个引用是否包含内存中的相同地址。
作为静态方法,它不能重写。
它认为null等于null
2 虚拟有Equals()方法
Equals()虚拟版本的System.Object实现代码也可以比较引用,但因为这个方法是虚拟的,所以可以在自己的类中重写它,从而按值来比较对象。
3 静态的Equals()方法
与虚拟版本区别是带有两个参数,并对它们进行相等性比较。这个方法可以处理两个对象中有一个是null的情况,因此,如果一个对象可能是null,这个方法就可以抛出异常,提供额外的保护。
静态重载版本首先要检查传递给它的引用是否为null。如果它们都是null,就运回true。如果只有一个就返回false。
4 比较运算符(==)
最好将比较运算符看作严格的值比较和严格的引用比较之间的中间选项。在大多数情况下,下面的代码表示正在比较引用:
bool b=(x==y);
如果把一些类看作值比较直观的话最好重载运算符。
7.4.2比较值类型的相等性
略
7.5运算符重载
概念略
7.5.1运算符的工作方式
略
7.5.2运算符重载的示例
namespace A{
struct Vector{
public double x,y,z;
public Vector(double x,double y,double z){
this.x=x;
this.y=y;
this.z=z;
}
public Vector(Vector rhs){
x=rhs.x;
y=rhs.y;
z=rhs.z;
}
public override string ToString(){
return "{"+x+" "+y+" "+z+"}";
}
public static Vector operator + (Vector lhs,Vector rhs){
Vector result=new Vector(lhs);
result.x+=rhs.x;
result.y+=rhs.y;
result.z+=rhs.z;
return result;
}
}
}
//重载了+运算
static void Main(){
Vector vect1,vect2,vect3;
vect1=new Vector(3.0,3.0,1.0);
vect2=new Vector(2.0,-4.0,-4.0);
vect3=vect1+vect2;
Console.WriteLine(vect1.toString());
Console.WriteLine(vect2.toString());
Console.WriteLine(vect3.toString());
}
1添加更多的重载
略
2比较运算符的重载
3对:
==和!=
>和<
>=和<=
C#语言要求成对比较重载运算符,如果重载了"=="就要重载"!=",否则会产生编译错误。
而且,比较运算符必须返回布尔类弄的值。
3可重载的运算符
算术二元运算符 + * / - % 限制无
算术一元运算符 + - ++ -- 限制无
按位二元运算符 & | ^ << >> 限制无
按位一元运算符 ! ~ true false true和false运算符必须成对重载
比较运算符 == != > < >= <= 必须成对重载
赋值运算符 += -= *= /= >>= <<= %= &= |= ^= 不能显式地重载运算符,在重写单个运算符(如+,-,%等)时,它们会被隐式地重写
索引运算符 [] 不能重载
数据类型强制转换运算符 () 不能直接重载。用户定义的类型强制转换允许定义定制的类型强制转换行为
7.6用户定义的类型强制转换
概念略
7.6.1实现用户定义的类型强制转换
struct Currency{
pubilc uint Dollars;
public ushort Cents;
public Currency(uint dollars,ushort cents){
this.Dollars=dollars;
this.Cents=cents;
}
public override string ToString(){
return string.Format("${0}.{1,-2:00}",Dollars,Cents);
}
}
Currency balance=new Currency(10,50);
float f=balance;
上面需要加上
public static implicit operator float(Currency value){
return value.Dollars+(value.Cents/100.0f);
}
如果是float转Currency
public static implicit operator Currency(float value){
uint dollars=(uint)value;
ushort cents=(ushort)((value-dollars)*100);
return new Currency(dollars,cents);
}
下面代码能成功编译
float amount=45.63f;
Currency amount2=(Currency)amount;
下面代码会抛出一个编译错误,因为它试图隐式地使用一个显式的类型强制转换
float amount=45.63f;
Currency amount2=amount;
把数据类型强制转换声明为显式,就是警告开发人员要小心,因为可能会丢失数据
1类之间的强制转换
定义不同结构或类的不同实例之间的类型强制转换是完全合法的,但有两个限制:
如果某个类派生自另一个类 ,就不能定义这两个类之间的类型强制转换(这些类型的类型转换已经存在)
类型强制转换必须在源数据类型或目标数据类型的内部定义。
例:
A继承于System.Object,B继承于A,C和D继承于B
能合法自定义类型强制转换就是类C和D之间的转换,因为这些类并没有互相派生。
public static explicit opeartor D(C value){
//and so on
}
public static explicit operator C(D value){
//and so on
}
2基类和派生类之间的类型强制转换
MyDerived derivedObject=new MyDerived();
Mybase baseCopy=derivedObject;
上面成功
下面是另一种方式:
MyBase derivedObject=new MyDerived();
MyBase baseObject=new MyBase();
MyDerived derivedCopy1=(MyDerived)derivedObject();
MyDerived derivedCopy2=(MyDerived)baseObject();
上面都是合法的,但最后一句会抛出一个异常。
最后适的选项通常是定义一个派生类的构造函数,它以基类的实例作为参数,让这个构造函数完成相关的初始化。
class DerivedClass:BaseClass{
public DerivedClass(BaseClass rhs){
//初始化
}
}
3装箱和拆箱数据类型强制转换
略
7.6.2
多重类型强制转换
简单地说就是上面那个转float类型的例子
如果是long类型的话,这它会先转float,再转long
(2014.12.16)
(伟大的C#仍在继续中)
7.4 比较对象的相等性
7.4.1比较引用对象的相等性
System.Object定义了3个不同的方法,来比较对象的相等性,:ReferenceEquals()和两个版本的Equals(),再加上运算符(==),实际上4种。
1 ReferenceEquals()方法
ReferenceEquals()是一个静态方法,测试两个引用是否引用类的同一个实例,特别是两个引用是否包含内存中的相同地址。
作为静态方法,它不能重写。
它认为null等于null
2 虚拟有Equals()方法
Equals()虚拟版本的System.Object实现代码也可以比较引用,但因为这个方法是虚拟的,所以可以在自己的类中重写它,从而按值来比较对象。
3 静态的Equals()方法
与虚拟版本区别是带有两个参数,并对它们进行相等性比较。这个方法可以处理两个对象中有一个是null的情况,因此,如果一个对象可能是null,这个方法就可以抛出异常,提供额外的保护。
静态重载版本首先要检查传递给它的引用是否为null。如果它们都是null,就运回true。如果只有一个就返回false。
4 比较运算符(==)
最好将比较运算符看作严格的值比较和严格的引用比较之间的中间选项。在大多数情况下,下面的代码表示正在比较引用:
bool b=(x==y);
如果把一些类看作值比较直观的话最好重载运算符。
7.4.2比较值类型的相等性
略
7.5运算符重载
概念略
7.5.1运算符的工作方式
略
7.5.2运算符重载的示例
namespace A{
struct Vector{
public double x,y,z;
public Vector(double x,double y,double z){
this.x=x;
this.y=y;
this.z=z;
}
public Vector(Vector rhs){
x=rhs.x;
y=rhs.y;
z=rhs.z;
}
public override string ToString(){
return "{"+x+" "+y+" "+z+"}";
}
public static Vector operator + (Vector lhs,Vector rhs){
Vector result=new Vector(lhs);
result.x+=rhs.x;
result.y+=rhs.y;
result.z+=rhs.z;
return result;
}
}
}
//重载了+运算
static void Main(){
Vector vect1,vect2,vect3;
vect1=new Vector(3.0,3.0,1.0);
vect2=new Vector(2.0,-4.0,-4.0);
vect3=vect1+vect2;
Console.WriteLine(vect1.toString());
Console.WriteLine(vect2.toString());
Console.WriteLine(vect3.toString());
}
1添加更多的重载
略
2比较运算符的重载
3对:
==和!=
>和<
>=和<=
C#语言要求成对比较重载运算符,如果重载了"=="就要重载"!=",否则会产生编译错误。
而且,比较运算符必须返回布尔类弄的值。
3可重载的运算符
算术二元运算符 + * / - % 限制无
算术一元运算符 + - ++ -- 限制无
按位二元运算符 & | ^ << >> 限制无
按位一元运算符 ! ~ true false true和false运算符必须成对重载
比较运算符 == != > < >= <= 必须成对重载
赋值运算符 += -= *= /= >>= <<= %= &= |= ^= 不能显式地重载运算符,在重写单个运算符(如+,-,%等)时,它们会被隐式地重写
索引运算符 [] 不能重载
数据类型强制转换运算符 () 不能直接重载。用户定义的类型强制转换允许定义定制的类型强制转换行为
7.6用户定义的类型强制转换
概念略
7.6.1实现用户定义的类型强制转换
struct Currency{
pubilc uint Dollars;
public ushort Cents;
public Currency(uint dollars,ushort cents){
this.Dollars=dollars;
this.Cents=cents;
}
public override string ToString(){
return string.Format("${0}.{1,-2:00}",Dollars,Cents);
}
}
Currency balance=new Currency(10,50);
float f=balance;
上面需要加上
public static implicit operator float(Currency value){
return value.Dollars+(value.Cents/100.0f);
}
如果是float转Currency
public static implicit operator Currency(float value){
uint dollars=(uint)value;
ushort cents=(ushort)((value-dollars)*100);
return new Currency(dollars,cents);
}
下面代码能成功编译
float amount=45.63f;
Currency amount2=(Currency)amount;
下面代码会抛出一个编译错误,因为它试图隐式地使用一个显式的类型强制转换
float amount=45.63f;
Currency amount2=amount;
把数据类型强制转换声明为显式,就是警告开发人员要小心,因为可能会丢失数据
1类之间的强制转换
定义不同结构或类的不同实例之间的类型强制转换是完全合法的,但有两个限制:
如果某个类派生自另一个类 ,就不能定义这两个类之间的类型强制转换(这些类型的类型转换已经存在)
类型强制转换必须在源数据类型或目标数据类型的内部定义。
例:
A继承于System.Object,B继承于A,C和D继承于B
能合法自定义类型强制转换就是类C和D之间的转换,因为这些类并没有互相派生。
public static explicit opeartor D(C value){
//and so on
}
public static explicit operator C(D value){
//and so on
}
2基类和派生类之间的类型强制转换
MyDerived derivedObject=new MyDerived();
Mybase baseCopy=derivedObject;
上面成功
下面是另一种方式:
MyBase derivedObject=new MyDerived();
MyBase baseObject=new MyBase();
MyDerived derivedCopy1=(MyDerived)derivedObject();
MyDerived derivedCopy2=(MyDerived)baseObject();
上面都是合法的,但最后一句会抛出一个异常。
最后适的选项通常是定义一个派生类的构造函数,它以基类的实例作为参数,让这个构造函数完成相关的初始化。
class DerivedClass:BaseClass{
public DerivedClass(BaseClass rhs){
//初始化
}
}
3装箱和拆箱数据类型强制转换
略
7.6.2
多重类型强制转换
简单地说就是上面那个转float类型的例子
如果是long类型的话,这它会先转float,再转long
(2014.12.16)