密封(sealed)类和方法
sealed关键字可以用来修饰类、变量和方法。sealed修饰符放在类之前,就将类声明为密封类,表示该类不能被继承。密封类主要防治意外的派生,同时可以提高程序的运行性能。在非密封类中,如果要防止一个实例成员在子类中被重写,就可以将该成员声明为密封成员,包括密封方法、密封属性、密封索引器等。对于密封方法,它只能用于对基类的虚方法进行重写,并提供具体的实现。所以在密封方法的声名中,sealed修饰符总是和override修饰符同时使用,以防止派生类进一步重写该方法。
比如:
1 using System; 2 class A 3 { 4 public virtual void F() 5 { 6 Console.WriteLine("A.F"); 7 } 8 9 public virtual void G() 10 { 11 Console.WriteLine("A.G"); 12 } 13 } 14 15 class B : A 16 { 17 sealed override public void F() 18 { 19 Console.WriteLine("B.F"); 20 } 21 22 override public void G() 23 { 24 Console.WriteLine("B.G"); 25 } 26 } 27 28 class C : B 29 { 30 override public void G() 31 { 32 Console.WriteLine("C.G"); 33 } 34 }
类B对于基类A中的两个虚方法均进行了重载,其中,F方法使用了sealed修饰符,成为了一个密封方法。G方法不是密封方法,所以在B类的派生类C中,可以重载G方法,但是不能重载F方法。
-
访问控制(Access Control)
关键字 | 说明 |
public | 所有类均可访问 |
private | 只能被它所在的类内部访问 |
protected | 可以被类内和所有子类访问 |
internal | 限定在类所在的程序内 |
-
抽象类与抽象方法
在现实生活中我们发现,一个类的某个或某几个方法是无法具体实现的。例如图形Shape类中的画图draw()方法,当你不知道具体要画什么形状,比如长方形或者三角形时,怎么可能实现一个画形状的方法呢?在C#中,这样的方法叫做抽象方法,一个类如果有一个或多个抽象方法,这个类就叫做抽象类。
用abstract关键字来修饰一个类时,该类叫做抽象类;用abstract来修饰一个方法时,该方法叫做抽象方法。
-
抽象类必须被继承,抽象方法必须被重写。
-
抽象类不能被直接实例化,因此它一般用作其他类的基类。
-
抽象方法只须声明,而无须实现。定义了抽象方法的类必定是抽象类。
实例:两个类Circle和Rectangle,计算面积:
class Circle
{
public float r;
Circle(float r)
{
this.r = r; // this指"这个对象"
}
public float Area()
{
return (float)3.14 * r * r;
}
}
class Rectangle
{
public float width, height;
Rectangle(float w,float h)
{
this.width = w;
this.height = h;
}
public float Area()
{
return width * height;
}
}
假设有若干个Circle以及若干个Rectangle,希望计算它们的面积,直截了当的做法是将他们分别放在2个数组中,用两个循环,加上一个加法,但这种方法不是最好的。因为如果还有其他形状,日triangle、ellipse等,上述方法显得"累赘"。希望有一种统一的表示,例如用给一个数组Shape[],接收所有的形状,然后用
1 foreach(Shape shape in sps) 2 { 3 area_total = shape.Area(); 4 }
完成调用。这样的话,我们仅需要一个抽象类Shape。
实例如下:
abstract class Shape
{
public abstract float Area();
}
class Circle : Shape
{
public float r;
public Circle(float r)
{
this.r = r;
}
public override float Area()
{
return (float)3.14 * r * r;
}
}
class Rectangle : Shape
{
public float width, height;
public Rectangle(float width, float height)
{
this.width = width;
this.height = height;
}
public override float Area()
{
return width * height;
}
}
public class AbstractShapeTest
{
static void Main(string[] args)
{
Shape circle1 = new Circle((float)2.0);
Shape rectangle1 = new Rectangle((float)3.4, (float)6.8);
Console.WriteLine("Circle's Area is:", circle1.Area());
Console.WriteLine("Rectangle's Area is:", rectangle1.Area());
Console.ReadKey();
}
}
// output:
// Circle's Area is:12.56
// Rectangle's Area is:23.12
接口(interface)
从本质上讲,接口是一种特殊的抽象类,这种抽象类中指定实现该接口的类必须提供的成员,实现接口的类提供接口成员的实现。那么为什么要使用接口?
-
通过接口可以实现不相关类的相同行为,而无须考虑这些类之间的层次关系。
-
通过接口可以指明多个类需要实现的方法。
-
通过接口可以了解对象的交互界面,而无须了解对象对应的类。
另外,从软件设计的角度来看,我们希望把复杂的应用系统分割为多个模块,每个模块完成独立的功能,模块之间能够协同工作,这样的模块称为组件。组件可以单独开发、编译和测试,把所有的功能结合到一起,就得到了完整的系统。要使组件能够协同工作,就必须在组件之间提供一种工作协议,而接口就非常适合作为组件之间的协议描述工具。
-
接口的定义
接口定义格式如下:
[接口修饰符] interface 接口名 [:基接口列表]
{
// 接口成员
}
其中,接口修饰符是可选的,只允许为new修饰符或者访问修饰符public、protected、internal或protected。基接口也是可选的,接口可以从零个或多个接口继承。interface和接口名是必需的,接口名是一种标示符,应符合标示符的命名规则,最好能体现接口的含义和用途。根据命名惯例,接口名总是以大写字母I开头。
接口成员具有如下要求:
-
接口成员必须是方法、属性、事件或索引器。接口不能包含常量、字段、运算符和实例构造函数、析构函数或类型,也不能包含类的静态成员。
-
接口只包含方法、属性、事件或索引器的签名,而不提供他们的实现。
-
接口成员都是public类型的,但是不能使用public修饰。
下面是接口的一个实例:
1 interface ITest 2 { 3 string Name 4 { 5 get; 6 set; 7 } 8 void MyMethod(); 9 }
接口可实现多重继承,即一个接口可以继承多个接口,在冒号后跟上一个由逗号分开的基接口列表,例如:
1 interface ITest1 2 { 3 void M1(); 4 } 5 6 interface ITest2 7 { 8 void M2(); 9 } 10 11 interface ITest3 12 { 13 void M3(); 14 } 15 16 interface ISubTest : ITest1, ITest2, ITest3 17 { 18 // 19 }
ISubTest将多重继承ITest1、ITest2和ITest3,将同时具备方法M1、M2和M3。
-
接口的实现
在类中继承接口叫做对接口的实现(Implementation),例如:
1 interface Collection 2 { 3 //方法是共有的public,C#编译器会自动加上public关键字 4 void Add(Object obj); 5 void Delete(Object obj); 6 Object Find(Object obj); 7 int CurrentCount(); 8 } 9 10 11 12 class Queue : Collection 13 { 14 public void Add() 15 { 16 // ... 17 } 18 19 public void Delete() 20 { 21 // ... 22 } 23 24 public Object Find(Object obj) 25 { 26 // ... 27 } 28 29 public int CurrentCount() 30 { 31 // ... 32 } 33 }
再看一个完整的示例,为了演示把之前使用的Shape抽象类变为接口:
1 interface IShape 2 { 3 void Draw();// 不能有任何实现的部分 4 //{ 5 // System.Console.WriteLine("draw in shape"); 6 //} 7 } 8 9 class Circle : IShape 10 { 11 private double r; 12 public Circle(double r) 13 { 14 this.r = r; 15 } 16 17 public void Draw() 18 { 19 Console.WriteLine("draw in circle,r={0}", r); 20 } 21 } 22 23 class Rectangle : IShape 24 { 25 private double width; 26 private double height; 27 28 public Rectangle(double width, double height) 29 { 30 this.width = width; 31 this.height = height; 32 } 33 34 public void Draw() 35 { 36 Console.WriteLine("draw in rectangle,width={0},heigth={1}", width, height); 37 } 38 }