一:继承的分类 从c#中的继承可以分为两种(其他面向对象的语言貌似也是这样) 实现继承(不能实现多重继承,子类只能从一个父类派生) 接口继承(可以从多个接口派生) 结构是值类型的,不支持实现继承,支持接口继承,可以派生于多个接口 二:修饰符
public | 任何代码均可访问 |
---|---|
protected | 仅派生类型可访问,实例对象不可访问 |
internal | 程序集内部可访问 |
private | 只能在类内部访问 |
protected internal | 程序集内部派生类中 |
new | 用相同的签名覆盖基类中的成员 |
static | 成员不在类的具体实例上执行 |
virtual | 成员可以由派生类重写 |
abstract | 只定义成员的签名,没有实现代码 |
override | 该成员重写了基类中的相同签名的virtual成员,并允许被再次重写 |
sealed | 该成员重写了基类中的相同签名的virtual成员,并不允许被再次重写 |
三: 子类拥有父类的所有子类可见成员 这也是设计子类的目的之一,为了扩展父类的功能
四:重写 方法只要声明为virtual,就可以被子类中签名相同的方法重写(override) 当然子类也可以不重写这个方法 成员字段和静态函数都不能被声明为virtual 一个方法被修饰成override,这个方法仍旧可以被它的子类重写
五:覆盖 用new关键字可以隐藏基类的方法,字段 这个感觉没什么好说的 综合例子一(看输出结果之前,希望你能仔细想以想)
public class baseClass
{
public void Methord()
{
Console.WriteLine("methord from base");
}
public virtual void Methord2()
{
Console.WriteLine("virtual methord from base");
}
}
public class sonClass : baseClass
{
public new void Methord()
{
Console.WriteLine("methord form son");
}
public override void Methord2()
{
Console.WriteLine("override methord from son");
}
}
class Program
{
static void Main(string[] args)
{
baseClass b = new baseClass();
b.Methord();
b.Methord2();
Console.WriteLine();
sonClass s = new sonClass();
s.Methord();
s.Methord2();
Console.WriteLine();
baseClass b2 = new sonClass();
b2.Methord();
b2.Methord2();
Console.WriteLine();
sonClass s2 = (sonClass)b2;
s2.Methord();
s2.Methord2();
Console.WriteLine();
Console.ReadKey();
}
}
输出结果为:
很多面试题中都有类似的题 我做个总结 一般情况下变量的类型是什么,该变量就拥有什么类型的成员 即使像下面这种情况也不例外 baseClass b2 = new sonClass(); b2的成员是baseClass中的成员 b2与sonClass中的成员无关 只有一种情况除外 当父类中的virtual方法已经被子类中的方法override过之后(new重写过之后是不行的) 类似这种情况baseClass b2 = new sonClass(); b2拥有的是重写过的方法成员 具体的原理以后有机会分析一下IL代码 那么我们总结一下这个现象 每个类型都有自己的类型成员表,虚方法成员是动态绑定的,运行时动态覆盖
综合例子二
public class A
{
public virtual void Fun1(int i)
{
Console.WriteLine(i);
}
public void Fun2(A a)
{
a.Fun1(1);
Fun1(5);
}
}
public class B : A
{
public override void Fun1(int i)
{
base.Fun1(i + 1);
}
public static void Main()
{
B b = new B();
A a = new A();
a.Fun2(b);
b.Fun2(a);
Console.ReadKey();
}
}
输出为
就不多解释了
六:通过base关键字获取基类的成员 看个比较特殊的例子
public class baseClass
{
public virtual void Methord2()
{
Console.WriteLine("virtual methord from base");
}
}
public class sonClass : baseClass
{
public override void Methord2()
{
base.Methord2();
Console.WriteLine("override methord from son");
}
}
class Program
{
static void Main(string[] args)
{
baseClass b2 = new sonClass();
b2.Methord2();
Console.ReadKey();
}
}
输出为:
由此可见重写方法是可以通过base关键字调用被重写的方法的 基类的成员表在重写方法中是可见的
七:抽象类和抽象方法(abstract) 抽象类不能实例化 抽象方法没有执行代码 如果类包含抽象方法,那么该类也必须声明为abstract 当然一个声明为abstract的类可以包含实例方法 抽象方法与虚方法类似,也是运行时动态绑定的 八:密封类和密封方法(sealed) 密封类不能被继承 sealed关键字只能修饰重写(override)方法 密封方法不能被重写 但是可以通过new关键字覆盖它 除非特殊情况最好少用这个关键字
九:继承关系中的构造函数 初始化一个类的实例的具体步骤是 1:初始化该类的字段 2:初始化基类的字段 3:初始化基类的构造函数 4:初始化该类的构造函数 可以通过base关键字制定初始化基类中的哪个构造函数 再看个综合例子,你觉得应该输出什么
class A
{
public A()
{
PrintFields();
}
public virtual void PrintFields(){}
}
class B : A
{
int x = 1;
int y;
public B()
{
y = -1;
PrintFields();
}
public override void PrintFields()
{
Console.WriteLine("x={0},y={1}", x, y);
}
}
class Program
{
static void Main(string[] args)
{
A b = new B();
Console.ReadKey();
}
}
输出为:
就不多解释了
十:接口继承 接口继承和实现继承其实差不多 做几点说明: 1.一个类可以实现多个接口 2.不允许提供接口中任何成员的实现方式 3.接口只能包含方法,属性,所引器和事件,不允许包含运算符重载 4.不能实例化接口,因此接口不能有构造函数 5.不能声明成员修饰符,接口成员总是公共的,也不能声明成员为虚拟的或者静态的,这些是具体实现的时候做的事情
做此文得到了郑州的Xuefly的支持,在此表示感谢