C# Interface、Abstract与Virtual用法详解及区别对比

小岛来信
小岛来信
发布时间:2026-10-14 | 3人浏览过
原创

在C#面向对象编程中,理清接口(Interface)、抽象(Abstract)与虚方法(Virtual)的关系是掌握多态性的关键。本文结合代码实例,深入解析三者的定义规则、实现要求以及在派生类中的行为差异。

一、 Interface:纯契约声明

interface主要用于定义一组行为规范(契约),不包含任何实现细节。其核心特点包括:

  1. 仅定义规约:只提供方法签名,严禁提供方法主体。
  2. 默认公开且无修饰符:接口成员自动默认为 public,因此不能显式添加 public、abstract 等访问修饰符。
  3. 严格限制属性:接口内不能包含字段变量(Field)或构造函数。否则编译器将报错。
复制代码
public interface IPerson
{
   void getName(); // 正确:不包含方法主体
   void getAge(string s); // 正确:可包含参数

   // IPerson(); // 错误:接口不能有构造函数
   // string name; // 错误:接口不能有字段变量
   // public void getIDcard(); // 错误:接口不需要public修饰符
}

第1步:通过继承关系实现接口

类可以通过冒号 : 实现接口。实现类必须强制性地为接口中的所有方法提供具体实现逻辑,这与继承普通类的格式保持一致。

复制代码
public class Chinese : IPerson
{
   public Chinese() { /* 构造 */ }
   public void getName() { /* 实现getName */ }
   public void getAge(string s) { /* 实现getAge */ }
}

二、 Abstract:抽象类与方法

abstract既可以修饰类也可以修饰方法。它提供了一种介于纯粹接口和完整实现之间的形态。

  1. 抽象类约束:包含抽象方法的类必须是抽象类;抽象类无法直接实例化,必须由子类继承并实现抽象方法。
  2. 抽象方法特征:仅声明无主体,必须在派生类中用 override 关键字强制执行实现。

第2步:构建含抽象与虚方法的基类

在基类中同时定义抽象方法和虚方法(virtual),以此作为后续测试多态行为的样本。

复制代码
public abstract class Book
{
   public Book() { } // 可以包含普通构造函数

   public abstract void getPrice(); // 抽象方法:无主体,必须Override

   public virtual void getName()
   {
      Console.WriteLine("this is a test: virtual getName()");
   } // 虚方法:有主体,允许Override

   public virtual void getContent()
   {
      Console.WriteLine("this is a test: virtual getContent()");
   } // 虚方法:有主体,允许Override

   public void getDate() // 一般方法:若无virtual标记,需使用new隐藏
   {
      Console.WriteLine("this is a test: void getDate()");
   }
}

public class JavaBook : Book
{
   // 实现抽象方法(必须)
   public override void getPrice()
   {
      Console.WriteLine("JavaBook override abstract getPrice()");
   }

   // 覆盖虚方法(非必须)
   public override void getName()
   {
      Console.WriteLine("JavaBook override virtual getName()");
   }
}

三、 Virtual:虚方法与动态绑定

标记为 virtual 的方法允许派生类进行重写(Override)。如果派生类未选择重写,则默认调用基类版本。这与抽象方法(Abstract)有本质区别:抽象类不提供默认主体,而虚类提供默认主体且重写是可选的。

第3步:测试 Override 的多态表现

观察当对象声明为基类类型时,是否仍能调用派生类的重写方法。

复制代码
public class test
{
   public static void Main()
   {
      JavaBook jbook = new JavaBook();
      jbook.getPrice();  // 输出:JavaBook中重写内容
      jbook.getName();   // 输出:JavaBook中重写内容
      jbook.getContent(); // 输出:Book中默认内容(因JavaBook未重写此虚方法)
      jbook.getDate();   // 输出:Book中默认内容(无virtual标记,静态绑定)
   }
}

四、 Abstract 与 Virtual 的核心区别总结

两者在派生类重写时都使用 override 关键字,但在设计意图上截然不同:

  • Abstract 代表一种“强制”。它是骨架的一部分,要求子类必须填补空白。
  • Virtual 代表一种“扩展”。它提供了基础功能,允许子类在不破坏原有调用的前提下增强或改变行为。

验证 Abstract 强制实现

如果尝试移除 override 关键字来编译 Abstract 方法,程序将无法通过编译。

复制代码
public abstract class Book
{
   // 抽象方法,不含主体
   public abstract void Introduce();
}

public class JavaBook : Book
{
   // 必须强制添加 override 关键字,否则编译错误
   public override void Introduce()
   {
      Console.WriteLine("I'm Java");
   }
}

// ... 测试代码
Book book = new JavaBook();
book.Introduce(); // 此时动态绑定成功,调用子类的 Introduce()。

五、 New:显式隐藏而非重写

如果一个基类的方法既不是 abstract 也不是 virtual,而在派生类中使用了同名同参的方法,这种行为被称为“隐藏”(Hiding),必须使用 new 关键字明确标示(即便不加也会隐式编译警告)。

第4步:体验 Static Binding (静态绑定)

使用 new 关键字隐藏父类方法时,运行结果取决于对象的声明类型,而不是实际创建的对象类型。

复制代码
public abstract class Book
{
   public void SayHi() { Console.WriteLine("Hi, I'm book"); }
}

public class JavaBook : Book
{
   // 使用 new 显式隐藏父类方法,消除编译警告
   public new void SayHi() { Console.WriteLine("Hi, I'm Java"); }
}

public class test
{
   public static void Main()
   {
      JavaBook javaBook = new JavaBook();
      javaBook.SayHi(); // 调用 JavaBook.SayHi () (对象类型判定)

      Book book = new JavaBook(); // 声明为基类
      book.SayHi();            // 调用 Book.SayHi() (声明类型判定)
   }
}

以上就是对C#中Interface、Abstract与Virtual的详细解析及多态性对比,更多关于C#面向对象高级特性的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表