C#学习相关系列之abstract和virtual用法

简介: C#学习相关系列之abstract和virtual用法

一、abstract抽象类用法

1、抽象类的用途

       一个类设计的目的是用来被其他类继承的,它代表一类对象的所具有的公共属性或方法,那么这个类就应该设置为抽象类。

       抽象类是一种特殊的类,它不能被实例化,只能作为基类来派生出其它的具体类。抽象类使用abstract关键字来声明,其中可以包含抽象方法、虚方法、常规方法和属性。抽象类的主要作用是为其派生类提供一个通用的抽象基类。

       抽象方法是一种没有实现的方法,它只有定义并且声明,没有具体实现。抽象方法使用abstract关键字来声明,在抽象类中定义,而其具体实现必须在派生类中实现。抽象方法的主要作用是为其派生类提供一个统一的方法接口。

抽象类的使用场景:

  • 在类库中定义通用的抽象类作为基类,派生出具体的子类,可以提高代码的复用性,避免代码重复。
  • 在多态的环境下,使用抽象类和抽象方法定义统一的虚方法,可以让子类实现这些方法,减少繁琐的类型转换操作。
  • 抽象类和抽象方法的特点
  • 抽象类不能被实例化,只能用于派生具体类。
  • 抽象类可以包含抽象方法、虚方法、常规方法和属性。
  • 抽象方法必须在派生类中被实现,否则派生类也必须声明为抽象类。
  • 抽象方法必须被声明为public,因为派生类必须可以访问它。

2、抽象类的使用要求

  • abstract修饰符可用于类、方法、属性、索引和事件。
  • abstract修饰符用于简单地概括一个不完整的内容(摘要)。常用于基类,由派生类来继承实现基类的抽象属性和方法。
  • 抽象类是对具有相同行为和特征的抽象,抽象类中的成员不会指定具体细节,一般再其派生类中去实现具体内容。
  • 在普通类前加上abstract修饰符即可标注为抽象类。 如果一个普通类中对其成员使用了abstract修饰符,则该类必须修饰为抽象类。
  • 无法使用sealed修饰符来修改抽象类,因为两个修饰符的含义相反。sealed修饰符阻止类被继承,而abstract修饰符要求类被继承。
  • 继承抽象方法的派生类,必须实现override抽象类中的所有成员,用override重写来实现。
  • 抽象类无法被实例化,但可以让派生类实例化。
  • 抽象方法必须声明在抽象类中,声明抽象方法时,不能使用virtual、static、private修饰符。
  • 抽象方法声明不提供实际的声明,因此没有方法主体,和接口类似,和虚方法不一样。

3、抽象类和接口的区别

相同点:

1、不能实例化;

2、包含未实现的方法声明

3、派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员)

不同点:

1、接口可以多继承,抽象类不能实现多继承。

2、接口只能定义抽象规则,抽象类既能定义抽象规则也能提供已实现的成员。

3、接口是一组行为规范,抽象类是一个不完全的类,着重于族的概念。

4、接口支持回调,抽象类不能实现回调,因为继承不支持。

5、接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法,抽象类可以定义属性、字段、包含有实现的方法。

6、接口可以作用于值类型和引用类型,抽象类只能作用于引用类型(例如:Struct只能继承接口)。

7、抽象类应主要用于关系密切的对象,而接口则是适合为不相关的类提供通用功能。

8、接口着重于Can—Do关系类型,抽象类则偏重于IS—A式关系。

9、接口多定义对象的行为,抽象类多定义对象的属性。

10、如果要设计小而简练的功能块,则使用接口,如果要设计大的功能单元,则使用抽象类。

11、接口对应是面向对象编程思想的重要原则:面向接口编程。

4、抽象类的示例

public abstract class Shape
{
    public abstract double Area { get; }
}
public class Square : Shape
{
    private double side;
    public Square(double s)
    {
        side = s;
    }
    public override double Area
    {
        get { return side * side; }
    }
}

       abstract可以用来修饰类、方法、属性、索引器和事件,但不包括字段。使用abstract修饰的类,该类只能作为其他类的基类,不能实例化。而且abstract修饰的成员在派生类中必须全部实现,不允许部分实现,否则编译异常。

       抽象类和抽象方法是C#编程中的两个重要概念,它们提供了面向对象编程的许多特性。通过抽象类和抽象方法,我们可以定义通用的属性和行为,然后让具体的子类来继承并实现它们。这样可以提高代码的复用性、可维护性和可扩展性,从而使我们的应用程序更加灵活和可维护。

二、virtual虚函数的用法

1、 虚函数的作用

       当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法,虚方法是使用关键字virtual声明的,虚方法可以在不同的继承类中有不同的实现,即为基类中定义的允许在派生类中重写的方法;

定义如下:

访问修饰符 virtual 函数返回类型 函数名(参数表) {函数体};
class Person
    {
        public virtual void XXX()//虚方法
        {
            Console.WriteLine("我有一个梦想,那就是动身去往远方!!");
        }
    }
     class Boy : Person//继承person
    {
        public override void XXX()//虚方法调用
        {
            base.XXX();
        }
    }

       调用上,使用子类构造的对象调用虚方法,就会调用子类的方法,使用父类构造的对象,就会调用父类的方法;在基类中声明的虚方法一般在派生类中对其进行调用,会运用到base关键字:

2、虚函数的使用要求

1、 虚拟类其实指的是正常类中的虚拟方法,所以虚拟类可以直接使用实例,这是与抽象类不同的一点;

2、 虚拟方法是在方法前加virtual关键字,对方法进行声明,说明这个方法为虚方法;

3、虚拟函数和正常函数无区别;

4、子类继承虚拟类可以实现虚拟方法也可以不实现虚拟方法;

5、虚方法可以在不同的继承类中有不同的实现;

6、虚方法可以在不同的继承类中有不同的实现;

3、虚函数使用示例

public class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Animal speaks.");
    }
}
public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Dog barks.");
    }
}

1.允许 子类 / 派生类 进行重写;

2.增加代码的可维护性,脉络较清淅有条理;

三、override和new的使用

override

  • override是派生类用来重写基类方法的。调用的派生类方法,如需调用基类方法用base关键字
  • override不能重写非虚方法或静态方法。
  • override重写必须用abstract、override、virtrual修饰的方法。

new

  • new 是派生类用来隐藏基类方法的,在派生类中看不到基类方法,但调用的还是基类的方法。
  • 如果在派生类中要隐藏基类方法而不用new关键字,编译时会出现一个警告,提示如果是要隐藏基类中的方法,请使用new关键字。
  • new可以隐藏基类中的普通方法也可以是虚方法。
  • 如果在派生类中用private来修饰new 方法,那么只在该派生类中隐藏了基类中的方法,在该派生类之外,相当于没有隐藏基类中的方法;
  • 如果在派生类中隐藏了基类中的方法,在该派生类的派生类中,将延续对该派生类对基类方法的隐藏。
  • new 隐藏虚方法,重新生成本类与基类中方法同名的方法

代码1:

public class Program
    {
        static void Main(string[] args)
        {
            A a;
            B b = new B();
            C c = new C();
            b.Test();
            a = b;
            a.Test();
            a = c;
            a.Test();
            D d = new D();
            d.Test();
            Console.ReadLine();
        }
    }
    public class A
    {
        public virtual void Test()
        {
            Console.WriteLine("A Test()");
        }
    }
    public class B : A
    {
       public new void Test()
       {
            Console.WriteLine("B Test()");
       }
    }
    public class C : A
    {
        public override void Test()
        {
            Console.WriteLine("C Test()");
        }
    }
    public class D : C
    {
        public override void Test()
        {
            Console.WriteLine("D Test()");
        }
    }

代码2:

public class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.Test();
            B b = new B();
            b.Test();
            A c = new B();
            c.Test();
            A d = new C();
            d.Test();
            Console.ReadLine();
        }
    }
    public class A
    {
        public virtual void Test()
        {
            Console.WriteLine("A Test()");
        }
    }
    public class B : A
    {
        private new void Test()
        {
            Console.WriteLine("b Test()");
        }
    }
    public class C : B
    {
        public override void Test()
        {
            Console.WriteLine("C Test()");
        }
    }


相关文章
|
1月前
|
安全 C#
C# List基本用法
C# List基本用法
|
1月前
|
C#
C# Dev chartControl的用法
C# Dev chartControl的用法
|
2月前
|
Java 调度 C#
C#学习系列相关之多线程(一)----常用多线程方法总结
C#学习系列相关之多线程(一)----常用多线程方法总结
|
2月前
|
C#
C#学习相关系列之数据类型类的三大特性(二)
C#学习相关系列之数据类型类的三大特性(二)
|
2月前
|
C#
C#学习相关系列之yield和return的区别
C#学习相关系列之yield和return的区别
|
2月前
|
C#
C#学习系列相关之多线程(二)----Thread类介绍
C#学习系列相关之多线程(二)----Thread类介绍
|
2月前
|
C#
C#学习相关系列之数据类型类----嵌套类和嵌套方法(三)
C#学习相关系列之数据类型类----嵌套类和嵌套方法(三)
|
2月前
|
存储 C# 索引
C#学习相关系列之数据类型类的定义(一)
C#学习相关系列之数据类型类的定义(一)
|
2月前
|
C#
C#学习相关系列之自定义遍历器
C#学习相关系列之自定义遍历器
|
2月前
|
安全 C#
c#学习相关系列之as和is的相关用法
c#学习相关系列之as和is的相关用法