抽象类

前面我们已经使用到了虚方法(使用 Virtual修饰符)和抽象类及抽象方法(使用abstract修饰符)我们在多态一节中说到要实现类成员的重写必须定义为一个虚方法或抽象方法。这节单独把抽象类提出来,是因为抽象是.net实现面向对象编程重要的重要思想,定义抽象类就象一个模板一个提纲,它可以理解为中央的指导思想,我们通过派生类去具体实现它。由此可见,抽象类本身没有任何作用,也不能被实例化,因为他本身就不具有具体的事物。比如上节的动物类的例 子,我们实例化一个动物类并没有意义,我们需要实例的类是具体的动物,比如Dog、Cat、Sheep这些。

而我们使用面向对象编程,就要在具体的应用过程中,学会抽象出事物的共同点,形成一个基类,其它类去继承他,实现他。为我们提高编程、学习设计模式很有益处。

1.什么是抽象类

如果一个类不与具体的事物相联系,而只是表达一种抽象的概念,仅仅是作为其派生类的一个基类,这样的类就是抽象类,在抽象类中声明方法时,如果加上abstract时就是抽象方法

1.抽象类的声明

抽象类使用abstract关键词进行声明。

2.抽象类的特点:

A.抽象类不能被实例化

B.抽象类是一种特殊的类,具有类的特性,其成员可以是抽象的,也可以不是抽象的。

C.抽象方法必须定义在抽象类中,而不能定义在非抽象类中

D.抽象类可以继承一个抽象类,其派生类仍然是一个抽象类

E.抽象类不能被密封(不能使用sealed(sealed指该类不可被继承))

3.抽象方法的特点:

A.抽象方法是隐式的虚方法

B.抽象方法只允许声明在抽象类中

C.抽象方法不能提供实际的实现,所以没有方法体;抽象方法的实现是在非抽象的派生类中以override重写实现的

D.抽象方法声明中不可以使用static或者virtual修饰符

E.abstract关键字不能修饰静态方法或静态属性

6.抽象类的构造函数

A.不要再抽象类中定义public或protected internal访问权限的构造函数

B.应在抽象类中定义protected或private访问权限的构造函数

C.如果在抽象类中定义一个protected构造函数,则在实例化派生类时,基类可以执行初始化任务

7.抽象方法和虚方法的区别

虚方法(使用Virtual修饰符)有实现部分,并且派生类对其重写是可选的;抽象方法没有实现部分,并且强制非抽象派生类对其重写

8.抽象类和接口的区别

其实这个区别,本来是要在下一节,接口中讲的,在这里提前说一下,有点印象也好。

A.它们的派生类只能继承一个基类,即只能继承一个抽象类,但是可以继承多个接口。

B.抽象类中可以定义成员的实现,但接口中不可以。

C.抽象类中包含字段、构造函数、析构函数、静态成员或常量等,接口中不可以。

D.抽象类中的成员可以私有的(只要不是抽象的)、受保护的、内部的或受保护的内部成员,但接口中的成员必须是公共的。

PS:抽象类和接口这两种类型用于完全不同的目的。抽象类主要用作对象系列的基类,共享某些主要特性,例如共同的目的和结构。接口则主要用于类,这些类在基础水平上有所不同,但仍然可以完成某些相同的任务。

7.举例

我们举一个例子来说明一下抽象类的使用,还是我们前面的例子,基类动物(Animal),我们定义成一个抽象类,有几个派生类狗、猫、羊,假如我们猫还有波斯猫,狗还有牧羊犬,那么我们还可以让它们再次继承,因为猫和狗本身也是动物,我们可以不定义为抽象类,让他们支持实例化。下面看一下UML图:

关于UML图我会在后面抽时间写的,里面的成员+表示公有,-表示私有,字段、属性、方法使用分割线

下面是具体实现代码:

/// <summary>
/// 动物类(父类 抽象类)
/// </summary>
abstract class Animal
{
    /// <summary>
    /// 名字
    /// 说明:类和子类可访问
    /// </summary>
    protected string name;


    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="name"></param>
    public Animal(string name)
    {
        this.name = name;
    }

    private int shoutNum = 3;
    public int ShoutNum
    {
        get { return shoutNum; }
        set { shoutNum = value; }
    }

    /// <summary>
    /// 名字(虚属性)
    /// </summary>
    public virtual string MyName
    {
        get { return this.name; }

    }

    /// <summary>
    /// 叫声,这个方法去掉虚方法,把循环写在这里
    /// </summary>
    public void Shout()
    {
        string result = "";
        for (int i = 0; i < ShoutNum; i++)
            result += getShoutSound()+"!";

        Console.WriteLine(MyName);
        Console.WriteLine(result);
    }

    /// <summary>
    /// 创建一个叫声的虚方法,子类重写
    /// </summary>
    /// <returns></returns>
    public  virtual string  getShoutSound()
    {
        return "";
    }


}

/// <summary>
/// 狗(子类)
/// </summary>
class Dog : Animal
{
    string myName;
    public Dog(string name)
        : base(name)
    {
        myName = name;
    }

    /// <summary>
    /// 名字(重写父类属性)
    /// </summary>
    public override string MyName
    {
        get { return "我是:狗狗,我叫:" + this.name; }
    }

    /// <summary>
    /// 叫(重写父类方法)
    /// </summary>
    public override string getShoutSound()
    {
        return "汪!";
    }
}

/// <summary>
/// 狗(子类)
/// </summary>
class ShepherdDog : Dog
{
    string myName;
    public ShepherdDog(string name): base(name)
    {
        myName = name;
    }

    /// <summary>
    /// 名字(重写父类属性)
    /// </summary>
    public override string MyName
    {
        get { return "我是:牧羊犬,我叫:" + this.name; }
    }

    /// <summary>
    /// 叫(重写父类方法)
    /// </summary>
    public override string getShoutSound()
    {
        return "汪~呜!";
    }
}

/// <summary>
/// 猫(子类)
/// </summary>
class Cat : Animal
{
    string myName;
    public Cat(string name)
        : base(name)
    {
        myName = name;
    }
    /// <summary>
    /// 名字(重写父类属性)
    /// </summary>
    public override string MyName
    {
        get { return "我是:猫咪,我叫:" + this.name; }

    }

    /// <summary>
    /// 叫(重写父类方法)
    /// </summary>
    public override string getShoutSound()
    {
        return "喵!";
    }
}

    /// <summary>
/// 猫(子类)
/// </summary>
class PersianCat : Cat
{
    string myName;
    public PersianCat(string name): base(name)
    {
        myName = name;
    }
    /// <summary>
    /// 名字(重写父类属性)
    /// </summary>
    public override string MyName
    {
        get { return "我是:波斯猫,我叫:" + this.name; }

    }

    /// <summary>
    /// 叫(重写父类方法)
    /// </summary>
    public override string getShoutSound()
    {
        return "喵~呜!";
    }
}


/// <summary>
/// 羊(子类)
/// </summary>
class Sheep : Animal
{
    string myName;
    public Sheep(string name)
        : base(name)
    {
        myName = name;
    }
    /// <summary>
    /// 名字(重写父类属性)
    /// </summary>
    public override string MyName
    {
        get { return "我是:羊羊,我叫:" + this.name; }

    }
    /// <summary>
    /// 叫(重写父类方法)
    /// </summary>
    public override string getShoutSound()
    {
        return "咩!";
    }
}

调用及结果如下:

//调用
Animal persianCat = new PersianCat("好猫");
persianCat.Shout();
Console.ReadLine();
//结果如下:
//我是:波斯猫,我叫:好猫
//喵~呜!!喵~呜!!喵~呜!!

10要点:

抽象类是指:一个类不与具体的事物相联系,而只是表达一种抽象的概念,仅仅是作为其派生类的一个基类。

抽象类是一种特殊类,使用abstract声明

抽象类不可被实例化

抽象类可以继承一个抽象类

抽象方法只能存在于抽象类中,可以和虚方法一样,在派生类中重写。

抽象类的存在就是被继承用的,因此不允许被密封(即不能使用sealed修饰)

最后,抽象,是面向对象程序设计的重要思想。。