类继承
基类,派生类
public class MyClass:MyBase
{
}
C#中所有类的基类System.Object类(别名object)
C#为单继承
编译器不允许派生类的可访问性高于基类。即内部类可以继承一个公共基类,但公共类不能继承一个内部类。
隐藏基类成员
派生类不能删除它继承的任何成员,但它可以隐藏基类成员。
要隐藏一个继承的数据成员,需要声明一个新的相同类型的成员,并使用相同的名称。
通过在派生类中声明新的带有相同前面的函数成员,可以隐藏或掩盖继承的函数成员。注:签名由名称和参数列表组成,但不包括返回类型。
使用new修饰符故意隐藏继承的成员。没有new修饰符,程序可以成功编译。但会产生一个警告。说明隐藏了一个基类成员。
也可以隐藏静态成员。
例:
隐藏基类成员
public class MyBaseClass
{
string Field1;
}
public class MyDerivedClass: MyBaseClass
{
new string Field1; //用同样的名称掩盖基类成员
}
隐藏基类方法
public class MyBaseClass
{
public void DoSomething()
{
}
}
public class MyDerivedClass: MyBaseClass
{
new public void DoSomething()
{
}
}
基类访问
有时派生类需要访问被隐藏的继承成员。可以使用基类访问表达式访问隐藏的基类成员。
基类访问表达式 由关键字base后面跟着一个点和成员的名称组成: base.成员名称
使用基类的引用
派生类的实例由基类的实例加上派生类附加的成员组成。派生类的引用指向整个类对象,包括基类部分。
如果有一个派生类对象的引用,就可以获取该对象基类部分的引用,使用类型转换运算符把该引用转换为基类类型。类型转换运算符放置在对象引用的前面,用圆括号括起来的要被转换成的类名组成。
MyDerivedClass derived=new MyDerivedClass(); //创建一个对象 通过派生类
MyBaseClass mybc=(MyBaseClass)derived; //转换引用 派生类对象转换为基类
derived对象可以访问从new MyDerivedClass()语句创建的实例的所有部分。
mybc只能访问new MyDerivedClass()语句创建实例的基类相关的部分。
虚方法和覆写方法
虚方法可以使基类的引用访问“升至”派生类内。(注:不使用虚方法,当使用基类引用访问派生类对象时,得到的是基类的成员。)
可以使用基类引用调用派生类的方法,只需满足:
派生类的方法和基类方法有相同的签名和返回类型。
基类的方法使用virtual标注。
派生类的方法使用override标注。
class MyBaseClass
{
virtual public void Method()
{}
}
class MyDerivedClass:MyBaseClass
{
override public void Method()
{}
}
class Program{
static void main(){
MyDerivedClass derived=new MyDerivedClass();
MyBaseClass mybc=(MyBaseClass)derived;
derived.Method(); //调用MyDerivedClass的方法
mybc.Method(); //调用MyDerivedClass的方法
}
}
覆写和被覆写的方法必须有相同的访问性。
不能覆写static方法或非虚方法。
方法、属性、索引、事件都可以被声明为virtual和override。
覆写标记为override的方法
覆写方法可以在继承的任何层次出现。
当使用对象基类部分的引用调用一个覆写的方法时,方法的调用被沿派生层次上溯执行,一直到标记为override的方法的最派生版本。
如果在更高的派生级别有该方法的其他声明,但没有被标记为override,那么它们不会被调用。
例:
class MyBaseClass
{
virtual public void Method()
{}
}
class MyDerivedClass:MyBaseClass
{
override public void Method()
{}
}
class MySecondClass:MyDerivedClass
{
override public void Method()
{}
}
class Program
{
static void main(){
MySecondClass derived=new MySecondClass();
MyBaseClass mybc=(MyBaseClass)derived;
derived.Method(); //调用MySecondClass的方法
mybc.Method(); //调用MySecondClass的方法
}
}
例:
class MyBaseClass
{
virtual public void Method()
{}
}
class MyDerivedClass:MyBaseClass
{
override public void Method()
{}
}
class MySecondClass:MyDerivedClass
{
new public void Method()
{}
}
class Program
{
static void main(){
MySecondClass derived=new MySecondClass();
MyBaseClass mybc=(MyBaseClass)derived;
derived.Method(); //调用MySecondClass的方法
mybc.Method(); //调用MyDerivedClass的方法
}
}
构造函数的执行
要创建对象的基类部分,基类的一个构造函数被作为创建实例过程的一部分被调用。
继承层次链中的每个类在执行它自己的构造函数体之前执行执行它的基类构造函数。
对象构造的顺序:
初始化实例成员。-->调用基类构造函数。-->执行实例构造函数的方法体。
注意:
在构造函数中调用虚方法是极不推荐的。在基类的构造函数被执行时,基类的虚方法会调用派生类的覆写方法,但这是在派生类的构造函数方法体被执行之前。因此,调用会在派生类没有完成初始化之前传递到派生类。
构造函数初始化语句
默认情况下,在对象被构造时,基类的无参数构造函数被调用。但构造函数可以被重载,所有基类可能有一个以上的构造函数。如果希望派生类使用一个指定的基类构造函数,必须在构造函数初始化语句中指定它。
有两种形式的构造函数初始化语句:
1.使用base并指明使用哪一个基类。
2.使用this并指明应该是一当前类的哪一个另外的构造函数。
基类构造初始化语句放在冒号后面,冒号后跟类的构造函数声明的参数列表。构造函数初始化语句由关键字base和要调用的基类构造函数的参数列表组成。
例:
指定基类的构造函数
class MyClass:MyBaseClass
{
public MyClass(int i,int j):base(i)
{
}
}
使用this调用非默认构造器
class MyClass:MyBaseClass
{
public MyClass():this (i,j)
{
}
public MyClass(int i,int j):base(i)
{
}
}
若没有给构造函数指定构造函数初始化器,编译器会自动添加base()。
类访问修饰符
类的访问修饰符有两个级别:internal ,public
标记为public的类可以被系统内任何程序集中的代码访问。
public class MyClass
{
}
标记为internal的类只能被它自己所在的程序集内的类看到。
这是默认的可访问级别。所以,除非在类的声明中显示地指定修饰符public,程序集外部的代码不能访问该类。
可以使用internal访问修饰符显示地声明一个类为内部的。
internal class MyClass
{
}
程序集间的继承
C#允许从一个在不同的程序集内定义的基类派生类。要做到这一点,必须满足:
基类必须被声明为public,这样它才能被从它所在的程序集外部访问。
必须在Visual Studio工程中包括对包含该基类的程序集的引用。
为了使引用其他程序集中的类和类型更容易,不使用它们的完全限定名称,在源文件的顶部放置一个using指令
成员访问修饰符
public 公有的
protected 受保护的
internal protected 受内部保护的
private 私有的
internal 内部的
所有显示声明在类的声明中的成员都是相互可见的。
必须对每个成员指定访问级别。如果不指定某个成员的访问级别,它的隐式访问级别为private。
成员不能比它的类更可访问。即如果一个类的可访问性限于它所在的程序集,那么类的成员个体也不能从程序集外部看到。无论它的访问修饰符是什么。
访问成员的区域
public 公有的
所有类,程序集内部的类和外部的类都可以自由访问成员。
protected 受保护的
程序集外部不可访问,字段所在类可访问,字段所在类的派生类可访问。
internal protected 受内部保护的
对所有继承该类的类以及所有程序集内部的类可访问。
private 私有的
只能被自己的类的成员访问。
internal 内部的
对程序集内部的所有类可访问,但对程序集外部的类不可访问。
抽象成员
抽象成员是被设计来被覆写的函数成员。抽象成员有以下特征:
用abstract修饰符标记。
没有实现代码块。抽象成员的代码块被分号代替。
abstract public void PrintStuff(string s);
abstract public int MyProperty
{
get;
set;
}
不能把virtual修饰符加到abstract修饰符。
派生类中抽象方法的实现必须指定override修饰符。
抽象成员只能被声明在抽象类中。
抽象类
抽象类只能被用作其他类的基类。抽象类就是被设计来被继承的。
不能创建抽象类的实力
抽象类使用abstract修饰符。
抽象类可以包含抽象成员,但不必须。抽象类的成员可以是抽象成员和普通带实现的成员的任意组合。
抽象类自己可以派生自另一个抽象类。任何派生自抽象类必须使用override关键字实现该类所有的抽象成员,除非派生类自己也是抽象类。
密封类
密封类只能被用作独立的类,它不能被用作基类。
密封类使用sealed修饰符标注。
静态类
静态类的所有成员都是静态的。静态类用于封装不受实例数据影响的数据和函数。 静态类的一个普通的用途可能就是创建一个包含一组数学方法的数学库。
类本身必须标记为static。
类的所有成员必须是静态的。
类可以有一个静态构造函数,但没有实例构造函数,不能创建该类的实例。
不能继承静态类,它们是密封的。
扩展方法
如果不能修改类。例如该类在第三方类库中,那么只要它不是密封类,就能把它用作一个基类并在派生自它的类中实现这个增加的方法。
如果不能修改类,类又是密封的,或有其他的设计原因使这些方法不能工作,那么不得不在另一个类中使用该类的公有成员编写一个方法。
扩展方法必须被声明static。
扩展方法声明所在的类必须声明为static。
扩展方法必须包含关键字this作为它的第一个参数实型,并在后面跟着它所扩展的类的名称。
static class ExtendMyData
{
public static double Average(this MyDate md){
....
}
}
例:
namespace ExtensionMethods
{
sealed class MyData
{
private double D1,D2,D3;
public MyData(double d1,double d2,double d3)
{D1=d1;D2=d2;D3=d3;}
public double Sum(){return D1+D2+D3;}
}
static class ExtendMyData
{
public static double Average(this MyDate md){
return md.Sum()/3;
}
}
}
class Progarm
{
static void Main()
{
MyData md=new MyDate(3,4,5);
Console.WriteLine("Sum: {0}",md.Sum());
Console.WriteLine("Average:{0}",md.Average());
}
}
外部方法
外部方法是在声明中没有实现的方法。常常用于C#之外的语言编写的。
外部方法是用extern修饰符标记,而且类的声明中没有实现。最后跟分号。
声明和实现的连接是依赖实现的,但常常使用DllImport特性完成。
例:调用Win32 API
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace ExternalMethod
{
class MyClass
{
[DllImport(""kernel32,SetLastError=true)]
public static extern int GetCurrentDirectory(int a,StringBuilder b);
}
class Progarm
{
static void Main()
{
const int MaxDirLength=250;
StringBuilder sb=new StringBuilder();
sb.Length=MaxDirLength;
MyClass.GetCurrentDirectory(MaxDirLength,sb);
Console.WriteLine(sb);
}
}
}