写类和写接口的对比
3.7 枚举类型
S s = new S();
s.Foo();// 没有装箱
I i = s;// 当转换为接口时引发装箱 i.Foo();
写类和写接口的对比
指导原则:
当能自然地共享实现时,使用类和子类
•
当实现是独立的,为类定义接口
•
观察下面的类:
abstract class Animal {}
abstract class Bird : Animal {}
abstract class Insect : Animal {}
abstract class FlyingCreature : Animal {}
abstract class Carnivore : Animal {}
// 具体实现类:
class Osrich : Bird {}
class Eagle : Bird, FlyingCreature, Carnivore {} // 不合法 class Bee : Insect, FlyingCreature {}// 不合法
class Flea : Insect, Carnivore {}// 不合法
Eagle类、Bee类和Flea类无法编译,因为它们继承自多个类,这是非法的。为了解决这个 问题,我们必须把其中的某些类转换成接口。问题是转换哪个类呢?遵照一般准则,我们看 出所有昆虫类共享实现,所有鸟类共享实现,所以Insect和Bird还保持为类。相反,“能飞 的生物”的“能飞”是独立的机制,“食肉动物”的“食肉”是独立的机制,所以我们把 FlyingCreature和Carnivore转换成接口:
interface IFlyingCreature {}
interface ICarnivore {}
在特定的语义中,Bird和Insect可以对应Windows控件和Web控件,FlyingCreature和 Carnivore对应IPrintable和IUndoable。
3.7 枚举类型
枚举类型是一种特殊的数值类型,可以在枚举类型中定义一组命名的数值常量。例如:
public enum BorderSide { Left, Right, Top, Bottom } 使用枚举类型的方法如下:
BorderSide topside = BorderSide.Top;
bool isTop = (topside == BorderSide.Top); // true 每个枚举成员都对应一个整型数,默认情况下:
对应的数值是int型的
•
按枚举成员的声明顺序,自动指定的常量为0、1、2……
•
可以指定其他的整数类型代替默认类型,例如:
public enum BorderSide : byte { Left, Right, Top, Bottom } 也可以显式指定每个枚举成员对应的值:
public enum BorderSide : byte { Left = 1, Right = 2, Top = 10, Bottom = 11 }
提示: 编译器还支持显式指定部分枚举成员。没有指定的枚举成员,在最后一个显式指定的值的基础上递 增。上例等价于:
public enum BorderSide : byte
{ Left = 1, Right, Top = 10, Bottom }
3.7.1 枚举类型转换
枚举类型的实例可以和它对应的整型值互相显式转换:
int i = (int) BorderSide.Left;
BorderSide side = (BorderSide) i;
bool leftorRight = (int) side <= 2;
也可以显式地将一个枚举类型转换成另一个。假设HorizontalAlignment定义如下:
public enum HorizontalAlignment {
Left = BorderSide.Left, Right = BorderSide.Right, Center
}
两个枚举类型之间的转换通过对应的数值进行:
HorizontalAlignment h = (HorizontalAlignment) BorderSide.Right;
// 等价于:
HorizontalAlignment h = (HorizontalAlignment) (int)BorderSide.Right;
在枚举表达式中,编译器对数值0进行特别处理,不需要显式转换:
BorderSide b = 0; // 不用转换 if(b == 0) ...
在C#中创建类 对0进行特别管理原因有两个:
第一个枚举成员经常被用作“默认”值
•
在合并枚举类型中,
• 0表示不标识类型
3.7.2 标志枚举类型
枚举类型成员可以合并。为了避免混淆,合并枚举类型的成员要显式指定值,典型的增量为2。例如:
[Flags]
public enum BorderSides { Left = 0, Right = 2, Top = 4, Bottom = 8 } 使用位运算符操作合并枚举类型的值,例如|和&,它们作用在对应的整型数值上。
BorderSides leftRight = BorderSides.Left | BorderSides.Right;
if((leftRight & BorderSides.Left) != 0)
Console.WriteLine ("Includes Left"); // Includes Left string formatted = leftRight.ToString(); // "Left, Right"
BorderSides s = BorderSides.Left;
s|=BorderSides.Right
Console.WriteLine(s==leftRight); // True
s ^=BorderSides.Right; // 切换BorderSides.Right Console.WriteLine(s); // Left
依照惯例,当枚举类型元素被合并时,一定要应用Flags属性。如果声明了一个没有标注Flags属性 的枚举类型,枚举类型的成员仍然可以合并,但是当在该枚举实例上调用ToString方法时,输出一 个数值而非一组名字。
一般来讲,合并枚举类型通常用复数名而不用单数名。
为了方便起见,可以把合并的成员直接放在枚举的声明内:
[Flags]
public enum BorderSides {
None=0,
Left = 1, Right = 2, Top = 4, Bottom = 8 LeftRight = Left | right,
TopBottom = Top | Bottom, All = LeftRight | TopBottom }
3.7.3 枚举运算符
枚举类型可以使用的运算符有:
= == ! = < > <= >= + - ^ & | ~ += -= ++ -- sizeof
位运算符、算术运算符和比较运算符都返回对应整型值的运算结果。枚举类型和整型之间可以做加 法,但两个枚举类型之间不能做加法。
3.7.4 类型安全问题
请看下面的枚举类型:
public enum BorderSide { Left, Right, Top, Bottom }
因为枚举类型可以和它对应的整型值相互转换,枚举的真实值可能超出枚举类型成员的数值范围。
例如:
BorderSide b = (BorderSide) 12345;
Console.WriteLine (b);//12345 位操作和算术操作也会产生非法值:
BorderSide b = BorderSide.Bottom;
b++;// 不会报错
不合法的BorderSide枚举值会破坏如下程序:
void Draw(BorderSide side) {
if(side == BorderSide.Left) {...}
else if (side == BorderSide.Right) {...}
else if (side == BorderSide.Top) {...}
else {...} // 这里被当成BorderSide.Bottom
}
其中一个解决方案是再加一个else子句:
...
else if (side == BorderSide.Bottom)...
else throw new ArgumentException ("Invalid BorderSide:" + side, "side");
另一个解决方案是,先检查枚举值的合法性。静态方法Enum.IsDefined有此功能:
BorderSide side = (BorderSide) 12345;
Console.WriteLine (Enum.IsDefined (typeof (BorderSide), side)); // False
但是,Enum.IsDefined对标志枚举类型不起作用,然而下面的方法(使用Enum.ToString()),可 以在标志枚举类型合法时返回true。
static bool IsFlagDefined (Enum e) {
decimal d;
return !decimal.TryParse(e.ToString(), out d);
} [Flags]
public enum BorderSides { Left =1, Right = 2, Top =4, Bottom = 8 } static void Main()
{
for(int i = 0; i<=16; i++) {
BorderSide side = (BorderSide)i;
Console.WriteLine(IsFlagDefined(side) + " " + side);
} }
在C#中创建类