C# virtual 和 abstract 详解

简介

C# 中,virtualabstract 关键字都用于面向对象编程中的继承和多态,它们主要用于方法、属性和事件的定义,但在用法上存在一些重要的区别。

virtual 关键字

virtual 表示可重写的方法,但可以提供默认实现,派生类可以选择是否重写。

使用规则

  • 只能在类成员(方法、属性、事件)上使用,不能用于字段。
  • 必须有默认实现,子类可以选择是否 override 进行重写。
  • 在基类中调用时,调用的是基类的方法,但如果子类重写了,则会调用子类的实现(运行时多态)。
  • 不能用于 static 方法,但可以用于属性和事件。

示例:virtual 方法

using System;class BaseClass
{public virtual void ShowMessage(){Console.WriteLine("基类的 ShowMessage() 方法");}
}class DerivedClass : BaseClass
{public override void ShowMessage(){Console.WriteLine("子类的 ShowMessage() 方法");}
}class Program
{static void Main(){BaseClass obj1 = new BaseClass();obj1.ShowMessage(); // 输出: 基类的 ShowMessage() 方法DerivedClass obj2 = new DerivedClass();obj2.ShowMessage(); // 输出: 子类的 ShowMessage() 方法BaseClass obj3 = new DerivedClass();obj3.ShowMessage(); // 输出: 子类的 ShowMessage() 方法 (运行时多态)}
}

virtual 属性

class Animal
{public virtual string Name { get; set; } = "Unknown Animal";
}class Dog : Animal
{public override string Name { get; set; } = "Dog";
}class Program
{static void Main(){Animal a = new Dog();Console.WriteLine(a.Name); // 输出: Dog}
}

abstract 关键字

abstract 表示抽象成员,没有实现,必须在派生类中重写。它只能出现在 abstract 类中。

使用规则

  • 只能在 abstract 类中使用,不能用于 sealed 类(密封类)。
  • 没有方法体,子类必须 override 提供具体实现。
  • 抽象方法不能有 private 修饰符,但可以是 protectedpublic
  • 抽象方法在基类中不能有默认实现,必须在子类实现。
  • 抽象类本身不能被实例化,但可以有构造函数。

示例:abstract 方法

using System;abstract class Animal
{public abstract void MakeSound(); // 没有方法体,子类必须实现
}class Dog : Animal
{public override void MakeSound(){Console.WriteLine("汪汪汪!");}
}class Cat : Animal
{public override void MakeSound(){Console.WriteLine("喵喵喵!");}
}class Program
{static void Main(){Animal dog = new Dog();dog.MakeSound(); // 输出: 汪汪汪!Animal cat = new Cat();cat.MakeSound(); // 输出: 喵喵喵!}
}

abstract 属性

abstract class Animal
{public abstract string Name { get; set; }
}class Dog : Animal
{public override string Name { get; set; } = "Dog";
}class Program
{static void Main(){Animal a = new Dog();Console.WriteLine(a.Name); // 输出: Dog}
}

virtual vs abstract 的区别

关键字virtualabstract
是否有实现有默认实现没有默认实现,必须在子类实现
是否必须重写可以重写,也可以不重写必须被子类重写
是否能在非 abstract 类中使用可以只能用于 abstract 类
能否实例化可以实例化基类抽象类不能实例化
适用范围方法、属性、事件方法、属性、事件

什么时候用 virtual,什么时候用 abstract?

virtual

  • 适用于提供默认行为,但允许子类覆盖的场景。
  • 例如:基类提供 virtual 方法 SaveToDatabase(),子类可以重写也可以直接使用。

abstract

  • 适用于基类无法提供合理默认实现,必须由子类提供具体实现的情况。
  • 例如:Animal 类的 MakeSound() 方法,每种动物的叫声都不同,所以必须由子类实现。

综合示例

using System;abstract class Animal
{public abstract void MakeSound(); // 必须由子类实现public virtual void Sleep(){Console.WriteLine("动物正在睡觉...");}
}class Dog : Animal
{public override void MakeSound(){Console.WriteLine("汪汪汪!");}public override void Sleep(){Console.WriteLine("狗狗正在睡觉...");}
}class Program
{static void Main(){Animal myDog = new Dog();myDog.MakeSound(); // 汪汪汪!myDog.Sleep();     // 狗狗正在睡觉...}
}

virtual 方法如何调用基类方法?

当子类重写了 virtual 方法后,仍然可以在 override 方法中调用 基类的实现,这在 扩展父类功能 时特别有用。

在子类 override 方法中调用 base 方法

using System;class BaseClass
{public virtual void ShowMessage(){Console.WriteLine("基类的 ShowMessage() 方法");}
}class DerivedClass : BaseClass
{public override void ShowMessage(){base.ShowMessage();  // 先调用基类方法Console.WriteLine("子类的 ShowMessage() 方法");}
}class Program
{static void Main(){DerivedClass obj = new DerivedClass();obj.ShowMessage();}
}

输出:

基类的 ShowMessage() 方法
子类的 ShowMessage() 方法
  • 这里 base.ShowMessage(); 先执行基类的方法,再执行子类的逻辑。
  • 这种方式避免了完全覆盖,而是基于已有逻辑做扩展。

abstract 和 interface 的区别

C# 中,abstractinterface 都可以定义必须由子类实现的方法,但它们有一些关键区别。

abstractinterface
是否有方法实现不一定,可以有 abstract 也可以 virtualC# 8.0+ 支持默认实现,但大多数情况下没有
是否可以包含字段可以(非 static)不可以
是否可以有构造函数可以不可以
继承方式只能继承一个可以实现多个
abstract class Animal
{public abstract void MakeSound();public virtual void Sleep(){Console.WriteLine("睡觉中...");}
}interface IAnimal
{void MakeSound();void Sleep();
}class Dog : Animal, IAnimal  // 可以同时继承抽象类和接口
{public override void MakeSound(){Console.WriteLine("汪汪汪!");}public void Sleep(){Console.WriteLine("狗狗正在睡觉...");}
}

abstract class 适用于有共享逻辑的场景,而 interface 适用于不同类之间的通用行为。

virtual 和 abstract 结合使用

在某些情况下,抽象类可以定义 virtual 方法,让子类选择是否覆盖。

abstract class Shape
{public abstract void Draw(); // 必须重写public virtual void Move(){Console.WriteLine("Shape is moving...");}
}class Circle : Shape
{public override void Draw(){Console.WriteLine("画一个圆形");}public override void Move(){Console.WriteLine("圆形在移动...");}
}class Program
{static void Main(){Shape shape = new Circle();shape.Draw(); // 画一个圆形shape.Move(); // 圆形在移动...}
}
  • Draw() 是抽象方法,必须重写。
  • Move() 是虚方法,子类可以选择是否重写。

virtual 和 abstract 在性能上的考虑

C# 中,virtual 方法会在运行时通过虚方法表(VTable) 进行调用,而 abstract 只是提供一个强制实现的约定,子类实现后仍然是 virtual 方法。

普通方法调用(非 virtual

public class MyClass
{public void NormalMethod() { }
}// 直接调用,无额外开销。

virtual 方法调用

public class MyClass
{public virtual void VirtualMethod() { }
}// 通过 VTable 进行间接调用,可能稍微慢一点(但一般无影响)。

abstract 方法调用

public abstract class MyBaseClass
{public abstract void AbstractMethod();
}public class MyClass : MyBaseClass
{public override void AbstractMethod() { }
}// abstract 方法在子类实现后,仍然是虚方法(virtual),运行时仍然使用 VTable 机制。

sealed 和 override 一起使用

sealed 关键字可以阻止子类进一步重写 virtual 方法。

class Parent
{public virtual void Show(){Console.WriteLine("父类方法");}
}class Child : Parent
{public sealed override void Show(){Console.WriteLine("子类方法,不能再被重写");}
}class GrandChild : Child
{// 这里会报错,因为 `Show` 方法被 `sealed` 了// public override void Show() { }
}
  • 子类 Child 已经 sealed override,所以 GrandChild 不能再重写 Show() 方法。

  • 适用于限制特定方法的继承,比如框架设计时控制扩展性。

什么时候用 virtual,什么时候用 abstract?

virtual

  • 当希望提供一个默认实现,但允许子类根据需要重写时。

abstract

  • 当基类无法提供默认实现,必须要求子类强制实现时。

sealed override

  • 当希望限制某个方法的进一步重写时。

用 abstract + virtual 组合

  • 适用于部分方法必须实现,部分方法可以选择重写的情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/78779.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自动驾驶的数据集以及yolov8和yolop

项目背景 网络全部是分割了没有检测。 自动驾驶的车道线和可行驶区域在数据集中的表示 自动驾驶系统中的车道线和可行驶区域的表示方式主要有以下几种: 基于几何模型:使用几何模型来描述车道线和可行驶区域的形状和位置,例如直线、曲线、多…

Oracle DROP、TRUNCATE 和 DELETE 原理

在 Oracle 11g 中,DROP、TRUNCATE 和 DELETE 是三种不同的数据清理操作,它们的底层原理和适用场景有显著差异 1. DELETE 的原理 类型:DML(数据操作语言) 功能:逐行删除表中符合条件的数据,保留…

PCIe 5.0光学SSD原型问世!

近日,Kioxia Corporation(铠侠)、AIO Core Co., Ltd. 和 Kyocera Corporation(京瓷)联合宣布成功开发了一款支持 PCIe 5.0 接口的光学 SSD 原型。该技术旨在通过光接口替换传统的电接口,从而显著增加计算设…

SQL 查询中涉及的表及其作用说明

SQL 查询中涉及的表及其作用说明: 涉及的数据库表 表名别名/用途关联关系dbo.s_orderSO(主表)存储订单主信息(订单号、日期、客户等)dbo.s_orderdetailSoD(订单明细)通过 billid SO.billid 关…

C++学习之金融类安全传输平台项目git

目录 1.知识点概述 2.版本控制工具作用 3.git和SVN 4.git介绍 5.git安装 6.工作区 暂存区 版本库概念 7.本地文件添加到暂存区和提交到版本库 8.文件的修改和还原 9.查看提交的历史版本信息 10.版本差异比较 11.删除文件 12.本地版本管理设置忽略目录 13.远程git仓…

【HCIP】GRE VPN实验笔记

一、实验拓扑 二、实验要求 1、按照图示配置IP地址 2、在R1和R3上配置默认路由使公网区域互通 3、在R1和R3上配置GRE VPN,使两端私网能够互相访问,Tunnel口IP地址如图 4、在R1和R3上配置RIPv2或者ospf或者静态,来传递两端私网路由 三、实…

大模型——Llama Stack快速入门 部署构建AI大模型指南

Llama Stack快速入门 部署构建AI大模型指南 介绍 Llama Stack 是一组标准化和有主见的接口,用于如何构建规范的工具链组件(微调、合成数据生成)和代理应用程序。我们希望这些接口能够在整个生态系统中得到采用,这将有助于更轻松地实现互操作性。 Llama Stack 定义并标准化…

ALOPS智能化运维管理平台

AIOps(Artificial Intelligence for IT Operations)即智能运维,是将人工智能技术应用于 IT 运维管理领域,以实现自动化、智能化的运维决策和管理。以下是关于 AIOps 的详细介绍: 核心能力 数据收集与整合&#xff1a…

C语言超详细指针知识(二)

在上一篇有关指针的博客中,我们介绍了指针的基础知识,如:内存与地址,解引用操作符,野指针等,今天我们将更加深入的学习指针的其他知识。 1.指针的使用和传址调用 1.1strlen的模拟实现 库函数strlen的功能是…

一种替代DOORS在WORD中进行需求管理的方法 (二)

一、前景 参考: 一种替代DOORS在WORD中进行需求管理的方法(基于WORD插件的应用)_doors aspice-CSDN博客 二、界面和资源 WORD2013/WORD2016 插件 【已使用该工具通过第三方功能安全产品认证】: 1、 核心功能 1、需求编号和跟…

设计模式 Day 6:深入讲透观察者模式(真实场景 + 回调机制 + 高级理解)

观察者模式(Observer Pattern)是一种设计结构中最实用、最常见的行为模式之一。它的魅力不仅在于简洁的“一对多”事件推送能力,更在于它的解耦能力、模块协作设计、实时响应能力。 本篇作为 Day 6,将带你从理论、底层机制到真实…

文献总结:AAAI2025-UniV2X-End-to-end autonomous driving through V2X cooperation

UniV2X 一、文章基本信息二、文章背景三、UniV2X框架1. 车路协同自动驾驶问题定义2. 稀疏-密集混合形态数据3. 交叉视图数据融合(智能体融合)4. 交叉视图数据融合(车道融合)5. 交叉视图数据融合(占用融合)6…

2025蓝桥杯python A组题解

真捐款去了,好长时间没练了,感觉脑子和手都不转悠了。 B F BF BF 赛时都写假了, G G G 也只写了爆搜。 题解其实队友都写好了,我就粘一下自己的代码,稍微提点个人的理解水一篇题解 队友题解 B 思路: 我…

免费送源码:Java+ssm+MySQL 校园二手书销售平台设计与实现 计算机毕业设计原创定制

摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对校园二手书销售平台等问题,对校…

工业科学级天文相机:跨界融合的高精密成像解决方案

随着国内科技的快速发展,工业相机领域正悄然兴起一场"天文级"的技术革命。这类兼具工业设备可靠性与天文观测精度的特殊相机,正在半导体制造、天文观测、空间探测等领域开辟新的应用疆域。其核心技术突破不仅体现在传感器性能的提升&#xff0…

论文阅读笔记——Multi-Token Attention

MTA 论文 在 Transformer 中计算注意力权重时,仅依赖单个 Q 和 K 的相似度,无法有效捕捉多标记组合信息。(对于 A、B 两个词,单标记注意力需要分别计算两个词的注意力分数,再通过后处理定位共同出现的位置或通过多层隐…

301.找出3位偶数

2094. 找出 3 位偶数 - 力扣&#xff08;LeetCode&#xff09; class Solution {List<Integer> resnew ArrayList<>();List<Integer> linew ArrayList<>();public int[] findEvenNumbers(int[] digits) {Arrays.sort(digits);boolean[] numsnew boolea…

【KWDB 创作者计划】第二卷:开发者实战篇

​KWDB技术白皮书卷二&#xff1a;开发者实战篇 ​1. 自然语言到量子查询的编译系统 1.1 NL2QSQL翻译引擎架构 运行时流程图解&#xff1a; ┌──────────────────────┐ ┌───────────────────┐ ┌─────────────…

前端工程化之新晋打包工具

新晋打包工具 新晋打包工具前端模块工具的发展历程分类初版构建工具grunt使用场景 gulp采用管道机制任务化配置与api简洁 现代打包构建工具基石--webpack基于webpack改进的构建工具rollup 推荐举例说明package.jsonrollup.config.mjsmy-extract-css-rollup-plugin.mjssrc/index…

ai软件UI自动化

在AI与UI自动化结合的场景中,通常涉及计算机视觉(CV)、自然语言处理(NLP)和机器学习(ML)等技术。以下是实现AI驱动UI自动化的关键方向、工具和步骤: ‌一、核心应用场景‌ ‌元素定位增强‌ ‌问题‌:传统工具依赖XPath/CSS选择器,易因UI变化失效。‌AI方案‌:CV识别…