• 沒有找到結果。

写类和写接口的对比

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#中创建类

相關文件