C# 教程 在线

2401C# 继承

关于基类访问(访问隐藏的基类成员)

Fields -- In the derived class

如果想要使得派生类能够完全访问被隐藏的继承成员,就可以使用基类访问表达式访问被隐藏的继承成员。

基类访问表达式由关键字base后面跟一个点和成员的名称组成。例如:

Console.WriteLine("{0}",base.Field1);

上面一行代码中的 base.Fields1 就属于基类访问。

基类访问的实例:

class SomeClass   //基类
{
    public string Field1 = "Fields -- In the base class";
}

class OtherClass : SomeClass  //继承类OtherClass,继承于SomeClass
{
    new public string Field1 = "Fields -- In the derived class";
    public void PrintField1()
    {
        Console.WriteLine(Field1);    //访问派生类
        Console.WriteLine(base.Field1)  //访问基类
    }
}


class Program
{
   static void Main()
   {
        OtherClass oc = new OtherClass();  //实例化
        oc.PrintFields1();                //执行oc的PrintFields1()方法
   }  
}

以上代码就是访问基类隐藏成员的实例,运行的结果是输出:

Fields -- In the derived class
Fields -- In the base class

2400C# 继承

为什么一个对象可以用父类声明,却用子类实例化

这个实例是子类的,但是因为你声明时是用父类声明的,所以你用正常的办法访问不到子类自己的成员,只能访问到从父类继承来的成员。

在子类中用 override 重写父类中用 virtual 申明的虚方法时,实例化父类调用该方法,执行时调用的是子类中重写的方法;

如果子类中用 new 覆盖父类中用 virtual 申明的虚方法时,实例化父类调用该方法,执行时调用的是父类中的虚方法;

/// <summary>  
/// 父类  
/// </summary>  
public class ParentClass  
{  
   public virtual void ParVirMethod()  
   {  
       Console.WriteLine("父类的方法...");  
   }  
}  

/// <summary>  
/// 子类1  
/// </summary>  
public class ChildClass1 : ParentClass  
{  
   public override void ParVirMethod()  
   {  
       Console.WriteLine("子类1的方法...");  
   }  
}  

/// <summary>  
/// 子类2  
/// </summary>  
public class ChildClass2 : ParentClass  
{  
   public new void ParVirMethod()  
   {  
       Console.WriteLine("子类2的方法...");  
   }  

   public void Test()  
   {  
       Console.WriteLine("子类2的其他方法...");  
   }  
}  

执行调用:

ParentClass par = new ChildClass1();  
par.ParVirMethod(); //结果:"子类1的方法",调用子类的方法,实现了多态

par = new ChildClass2();  
par.ParVirMethod(); //结果:"父类的方法",调用父类的方法,没有实现多态  

深究其原因,为何两者不同,是因为原理不同: override是重写,即将基类的方法在派生类里直接抹去重新写,故而调用的方法就是子类方法;而new只是将基类的方法在派生类里隐藏起来,故而调用的仍旧是基类方法。

应用举例

有这样的需要的,比如 People 类有一个 Run 方法,Man 和 Woman 这两个类都是继承自 People 的类,并且都重写(override)了 Run 这个方法(男人女人跑起步来不一样)。

现在有一群人一起跑步,有男人有女人。

我们可以把这些都装进一个People数组(假设为peoples)。

然后:

foreach(People p in peoples) // peoples中对象不同(即有男有女),用于实例化的子类就不同。
{
    p.Run(); // 故而,调用的方法也不同,实现了多态
}

由于多态性,在调用 p.Run() 的时候 p 对象本身如果是男人就会自动调用男人的 Run 方法,是女人就会调用女人的 Run 方法。

依赖倒置原则

依赖倒置原则,DIP,Dependency Inverse Principle DIP的表述是:

1、高层模块不应该依赖于低层模块, 二者都应该依赖于抽象。

2、抽象不应该依赖于细节,细节应该依赖于抽象。

这里说的“依赖”是使用的意思,如果你调用了一个类的一个方法,就是依赖这个类,如果你直接调用这个类的方法,就是依赖细节,细节就是具体的类,但如果你调用的是它父类或者接口的方法,就是依赖抽象, 所以 DIP 说白了就是不要直接使用具体的子类,而是用它的父类的引用去调用子类的方法,这样就是依赖于抽象,不依赖具体。

其实简单的说,DIP 的好处就是解除耦合,用了 DIP 之后,调用者就不知道被调用的代码是什么,因为调用者拿到的是父类的引用,它不知道具体指向哪个子类的实例,更不知道要调用的方法具体是什么,所以,被调用代码被偷偷换成另一个子类之后,调用者不需要做任何修改, 这就是解耦了。

2399C# 类(Class)

倘若在类的声明中没有显式地提供实例构造函数,在这种情况下编译器会提供一个隐式的默认构造函数,它具有以下特点:

①不带参数;

②方法体为空。

但是如果你声明了任何构造函数,那么编译器就不会把该类定义为默认构造函数。

例如:

class Class2
{
    public Class2(int Value)  {...}  //构造函数0
    public Class2(String Value) {...}  //构造函数1
}

class Program
{
    static void Main()
    {
        Class2 a = new Class2();   //错误!没有无参数的构造函数
        ...
    }
}

在以上的代码中至少有一个显式定义的构造函数,编译器不会创建任何额外的构造函数,在 Main() 中如果试图用不带参数的构造函数创建新的实例,因为没有无参数的构造函数,所以编译器就会产生一条错误信息。

2398C# 类(Class)

我们可以使用 static 关键字把类成员定义为静态的。当我们声明一个类成员为静态时,意味着无论有多少个类的对象被创建,只会有一个该静态成员的副本。

关键字 static 意味着类中只有一个该成员的实例。静态变量用于定义常量,因为它们的值可以通过直接调用类而不需要创建类的实例来获取。静态变量可在成员函数或类的定义外部进行初始化。你也可以在类的定义内部初始化静态变量。

将类成员函数声明为public static无需实例化即可调用类成员函数

反之,如果不声明为static,即使和Main方法从属于同一个类,也必须经过实例化

2397C# 类(Class)

将类成员函数声明为public static无需实例化即可调用类成员函数

using System;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            int num = AddClass.Add(2, 3);  //编译通过
            Console.WriteLine(num);
        }
    }

    class AddClass
    {
        public static int Add(int x,int y)
        {
            return x + y;
        }
    }
}

反之,如果不声明为static,即使和Main方法从属于同一个类,也必须经过实例化

using System;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            int num = Add(2, 3);  //编译错误,即使改为Program.Add(2, 3);也无法通过编译
            Console.WriteLine(num);
        }

        public int Add(int x, int y)
        {
            return x + y;
        }
    }
}
using System;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Program self = new Program();
            int num = self.Add(2, 3);  //编译通过
            Console.WriteLine(num);
        }

        public int Add(int x, int y)
        {
            return x + y;
        }
    }
}