.net基础---继承

1.继承的概念

  1.1概念

   现实生活中的事物都归属于一定得类别,比如,狮子是一种动物,为了在计算机中模拟这种关系,面向对象语言引入了继承(Inherit)的特性。

  如图所示,类Animal代表动物,类Lion代表狮子,空心三角箭头代表继承关系

  

  在构成继承关系的两个类中,Animal称为父类(Parent Class)或者基类(Base Class),Lion称为子类(Child Class)。

  父类和子类之间拥有以下两种基本关系:

    • 是一种(IS-A)关系:子类是父类的一种特例
    • 扩充关系:子类拥有父类所没有的功能

  1.2 在编程语言中实现继承:

  

1 class Animal
2 {
3 }
4 class Lion:Animal
5 {
6 }

 

 

  1.3 继承条件下类成员的访问权限

 

使用场合 C# 说明
Type(指类、接口等类型) public 访问不受限制
internal 访问范围限制同一程序集
Member(指类型中的成员,比如类中的字段) public 访问不受限制
internal 访问范围限制同一程序集
protected 访问范围限制于自己或派生出来的子类型
protected internal

在同一程序集内访问不受限制,在不同程序集中

仅由此类型派生出来的子类型可访问

private

仅自己可以访问


提示:C#中并非所有类都能被继承,声明为sealed的类不可继承,这种类被称为"密封类"。

2.类型转换

  子类对象可以被当成父类对象使用,因此以下代码是合法的

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Parent p;
 6             Son c = new Son();
 7             p = c;//正确,子类对象可以传给父类变量
 8         }
 9        
10     }
11     public class Parent
12     {
13     }
14     public class Son : Parent
15     { 
16     }

  然而反过来就不可以,以下代码是错误的:

 1 c=p;//错误,父类对象不能直接赋值给子类变量 

  如果确信父类变量中引用的对象是子类的类型,则可以通过强制转换进行赋值

 1 c=(Son)p; 

  或者是使用as运算符

 1 c=p as Son; 

  as运算符和强转的区别:

 1         static void Main(string[] args)
 2         {
 3             //************错误************
 4             Parent p1=new Son();
 5             Daughter d1;
 6             d1=(Daughter)p1;//这里运行会引发强制转换的异常
 7 
 8             //************正确************
 9             Parent p2 = new Son();
10             Daughter d2;
11             d2 = p2 as Daughter;//这里运行不会引发异常,但是会返回一个Null
12             if (d2 == null)
13             { 
14                 //空处理
15             }
16         }
17        
18     }
19     public class Parent
20     {
21     }
22     public class Son : Parent
23     { 
24     }
25     public class Daughter : Parent
26     { 
27     }

提示:类型转换的时候尽量避免使用强制转换,多用as避免异常,记住as转换完判断是否为空

3.方法重载、隐藏和虚方法的调用

  3.1 概念

  总的来说,子类方法和父类方法之间的关系可以概括为3种:

    • 扩充(Extend):子类定义的方法,在父类中没有同名的方法存在
    • 重载(Overload):子类方法与父类方法同名,但是参数列表不一样
    • 完全相同:子类方法与父类方法从方法名到参数完全一致

  3.2 重载

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Child c = new Child();
 6             c.OverloadF();//调用父类的方法
 7             c.OverloadF(100);//调用子类的重载方法
 8         }
 9        
10     }
11     public class Parent
12     {
13         public void OverloadF()
14         { 
15             
16         }
17     }
18     public class Child : Parent
19     {
20         public void OverloadF(int i)
21         { 
22         
23         }
24     }

  3.3 隐藏

  当子类与父类拥有完全一样的方法时,称"子类隐藏了父类的同名方法",当分别位于父类和子类的两个方法完全一样时,调用哪个方法由对象变量的类型决定

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Child c = new Child();
 6             Parent p;
 7             p = c;
 8             p.OverloadF();//调用的父类的同名方法,因为父类和子类方法重名,调用方法是由变量类型决定的,结果为parent.HideF()
 9             Console.ReadKey();
10         }
11        
12     }
13     public class Parent
14     {
15         public void OverloadF()
16         {
17             System.Console.WriteLine("Parent.HideF()");
18         }
19     }
20     public class Child : Parent
21     {
22         public void OverloadF()
23         {
24             System.Console.WriteLine("Child.HideF()");
25         }
26     }

  要是想调用子类方法,要先进行强制转换

 1 ((Child)p).HideF(); 

  前面定义parent和child,会发出一个警告,虽然警告并不影响运行结果,但是不符合C#的语法规范,修改child类定义如下:

1  public class Child : Parent
2     {
3         public new void  OverloadF()
4         {
5             System.Console.WriteLine("Child.HideF()");
6         }
7     }

  如果在子类方法中使用父类的隐藏同名方法,要使用base关键字

  3.4 重写与虚方法调用

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Child c = new Child();
 6             Parent p;
 7             p = c;
 8             p.OverrideF();//结果是Child.OverrideF(),调用父类还是子类的方法是由父类变量引用的真实对象类型决定的,和父类变量自身类型无关
 9             Console.ReadKey();
10         }
11        
12     }
13     public class Parent
14     {
15         public virtual void OverrideF()
16         {
17             System.Console.WriteLine("Parent.OverrideF()");
18         }
19     }
20     public class Child : Parent
21     {
22         public override void OverrideF()
23         {
24             System.Console.WriteLine("Child.OverrideF()");
25         }
26     }

  面向对象拥有的"虚方法调用"特性,使我们可以只用同样的一个语句,在运行时根据对象类型而执行不同的操作

  3.5 小心应用子类、父类间的隐藏和重写机制

 

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Parent p = new Parent();
 6             p.f();//Base.f()
 7             Child c = new Child();
 8             c.f();//Child.f()
 9             p = c;
10             p.f();//Child.f()
11             (p as Child).f();//Child.f()
12             (p as Parent).f();//Child.f()
13             Console.ReadKey();
14         }
15        
16     }
17     public class Parent
18     {
19         public virtual void f()
20         {
21             System.Console.WriteLine("Base.f()");
22         }
23     }
24     public class Child : Parent
25     {
26         public override void f()
27         {
28             System.Console.WriteLine("Child.f()");
29         }
30     }

 

  上面的例子说明在使用"虚方法"时,具体调用的方法决定于对象变量引用对象的真实类型

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Parent p = new Parent();
 6             p.f();//Base.f()
 7             Child c = new Child();
 8             c.f();//Child.f()
 9             p = c;
10             p.f();//Base.f()
11             (p as Child).f();//Child.f()
12             (p as Parent).f();//Base.f()
13             Console.ReadKey();
14         }
15        
16     }
17     public class Parent
18     {
19         public void f()
20         {
21             System.Console.WriteLine("Base.f()");
22         }
23     }
24     public class Child : Parent
25     {
26         public new void f()
27         {
28             System.Console.WriteLine("Child.f()");
29         }
30     }

  上面这个例子说明"隐藏",调用父类或者子类,不是由对象变量定义时的类型决定的,而是运行时的类型决定的

4.继承关系下访问字段

 

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Child c = new Child();
 6             Console.WriteLine(c.i);//200
 7             Parent p = new Parent();
 8             Console.WriteLine(p.i);//100
 9             p = c;
10             Console.WriteLine(p.i);//100
11         }
12        
13     }
14     public class Parent
15     {
16         public int i = 100;
17     }
18     public class Child : Parent
19     {
20         public int i = 200;
21     }

 

  从上面的例子可以看出:如果子类和父类有相同的字段,到底使用哪个字段,由对象变量定义的类型决定,而与程序运行时对象变量引用的真实对象类型无关(这个应该和隐藏方法一样)

  同样这样写也是不合规范的要加new,尽量避免使用隐藏字段

5.值类型

  所有的值类型都由一个特殊的类ValueType继承而来,而ValueType又继承自Object类,所以Object类也是所有值类型的父类。

  所有的值类型都不可以再派生出子类。

  值类型拥有一个隐含的构造函数,自动初始化其成员,.Net 规定数字类型变量初始化为0,C#要求所有值类型必须初始化才能使用,否则不能通过编辑

1    class Program
2     {
3         static void Main(string[] args)
4         {
5             int i;
6             i = i + 1;//无法通过编辑
7         }
8        
9     }

需改成:

 1   class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             //写法一
 6             int i = new int();
 7             i = i + 1;
 8             //写法二
 9             int i2 = 0;
10             i2 = i2 + 1;
11         }
12        
13     }

6.继承条件下的对象创建与销毁

  6.1 子类、父类构造函数的调用次序

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Child c = new Child();
 6             Console.ReadKey();
 7             //运行结果
 8             //Parent默认构造函数
 9             //Child默认构造函数
10         }
11        
12     }
13     public class Parent
14     {
15         public Parent()
16         {
17             Console.WriteLine("Parent默认构造函数");
18         }
19     }
20     public class Child : Parent
21     {
22         public Child()
23         {
24             Console.WriteLine("Child默认构造函数");
25         }
26     }

  上面例子可以看出,在创建子类对象时,首先调用父类的构造函数,在调用子类的构造函数,如果父类还有一个"祖父类"存在,则先会上溯到"祖父类",调用其构造函数,接着是父类,最后是子类,这种调用次序可以称为"尊重长辈"原则。

  当对象析构时,会首先调用子类析构函数,再是父类,刚好和创建对象相反。

  6.2 子类和父类构造函数的重载

   

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Child c = new Child("Hello");
 6             Console.ReadKey();
 7         
 8         }
 9     }
10     public class Parent
11     {
12         public Parent()
13         {
14             Console.WriteLine("Parent默认构造函数");
15         }
16         public Parent(string str)
17         {
18             Console.WriteLine("Parent带参构造函数被调用");
19         }
20        
21     }
22     public class Child : Parent
23     {
24         public Child()
25         {
26             Console.WriteLine("Child默认构造函数");
27         }
28         public Child(string str)
29             : base(str)
30         {
31             Console.WriteLine("Child带参构造函数被调用");
32         }
33       
34     }

 

 

 

 

posted on 2014-07-16 16:41  yutianu  阅读(331)  评论(1)    收藏  举报

导航