C# 多态 派生类 abstract virtual new

  • 静态多态
    • 函数重载
    • 运算符重载
  • 动态多态
    • abstract 和 virtual的区别
      • 定义与用途:
      • 成员实现:
      • 继承与重写:
      • 与接口的区别:
    • 使用抽象类的好处主要体现在以下几个方面:
      • 代码重用:
      • 设计灵活性:
      • 接口定义:
      • 类型安全:
      • 易于扩展和维护:
      • 符合面向对象的设计原则:
      • 综上所述,
  • 派生类中调用基类成员
      • 通过base关键字调用:
      • 直接调用:
      • 通过构造函数:
  • 在C#中,new关键字有多个用途
      • 对象实例化:
      • 隐藏基类成员:
      • 约束:
      • 运算符重载:
      • 初始化数组:
      • 随对象一起初始化字段:
  • 例子

静态多态

在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。C# 提供了两种技术来实现静态多态性。分别为:

函数重载

同名不同参

运算符重载

动态多态

C# 允许您使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。派生类具有更专业的功能。

请注意,下面是有关抽象类的一些规则:

您不能创建一个抽象类的实例。
您不能在一个抽象类外部声明一个抽象方法。
通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed

总之,
类前声明abstruct就可以写出一个抽象类

当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法。
虚方法是使用关键字 virtual 声明的。
虚方法可以在不同的继承类中有不同的实现。
对虚方法的调用是在运行时发生的。
动态多态性是通过 抽象类 和 虚方法 实现的。

在C#(以及其他一些面向对象的编程语言中),override是一个关键字,用于指示一个方法在派生类中覆盖了基类中的同名方法。这意味着,当你使用派生类的对象并调用这个方法时,将执行派生类中的版本,而不是基类中的版本。

要使用override关键字,必须满足以下条件

基类中存在一个虚方法(virtual)或抽象方法(abstract):基类中必须有一个具有相同签名(即相同的名称、参数列表和返回类型)的虚方法或抽象方法。

派生类继承自基类:包含override方法的类必须是从包含被覆盖方法的类继承而来的。

方法签名匹配:派生类中的方法必须与基类中被覆盖的方法具有相同的签名。

访问修饰符不能比基类方法更严格:派生类中方法的访问修饰符(如publicprotectedinternal)不能比基类中被覆盖的方法更严格。例如,如果基类中的方法是public的,那么派生类中的覆盖方法也必须是public的。

using System;
namespace PolymorphismApplication
{
   class Shape 
   {
      protected int width, height;
      public Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      public virtual int area()
      {
         Console.WriteLine("父类的面积:");
         return 0;
      }
   }
   class Rectangle: Shape
   {
      public Rectangle( int a=0, int b=0): base(a, b)
      {

      }
      public override int area ()
      {
         Console.WriteLine("Rectangle 类的面积:");
         return (width * height); 
      }
   }
   class Triangle: Shape
   {
      public Triangle(int a = 0, int b = 0): base(a, b)
      {
      
      }
      public override int area()
      {
         Console.WriteLine("Triangle 类的面积:");
         return (width * height / 2); 
      }
   }
   class Caller
   {
      public void CallArea(Shape sh)
      {
         int a;
         a = sh.area();
         Console.WriteLine("面积: {0}", a);
      }
   }  
   class Tester
   {
      
      static void Main(string[] args)
      {
         Caller c = new Caller();
         Rectangle r = new Rectangle(10, 7);
         Triangle t = new Triangle(10, 5);
         c.CallArea(r);
         c.CallArea(t);
         Console.ReadKey();
      }
   }
}

abstract 和 virtual的区别

C#中,abstractvirtual是两个用于类和成员修饰的关键字,它们各自有着特定的用途和区别。

定义与用途:

abstract关键字用于定义抽象类和抽象成员(包括方法、属性、索引器、事件)。抽象类是一种特殊的类,不能被实例化,只能被其他类继承。抽象成员在抽象类中没有具体的实现,必须在派生类中提供实现。
virtual关键字用于定义虚方法、虚属性、虚索引器或虚事件。虚成员可以在派生类中被重写(override)。这意味着派生类可以提供该成员的不同实现。

成员实现:

abstract修饰的方法或属性不能有实现,它们只是声明了一个约定,具体的实现必须在派生类中完成。
virtual修饰的方法或属性必须有方法实现(哪怕只有一对大括号)。派生类可以选择是否重写虚成员。

继承与重写:

如果一个类包含抽象方法,那么这个类必须被声明为抽象类。抽象方法必须在任何非抽象派生类中被重写。
虚方法可以被任何继承它的类重写,但不是必须的。如果派生类没有重写虚方法,那么将使用基类中的实现。

与接口的区别:

抽象类是对对象的抽象,可以包含抽象方法和非抽象方法,是对一组具有共同特征的对象的抽象。它不能被实例化,但可以通过继承来创建具体的对象。
接口是一种行为规范,只包含抽象方法和事件的声明,不包含任何实现。任何实现接口的类都必须提供接口中所有方法的实现。
总的来说,abstractvirtualC#中都是用于实现多态性的重要机制,但它们在定义、实现、继承与重写方面有着明显的区别。abstract更强调强制派生类提供实现,而virtual则提供了一种可选的重写机制。

使用抽象类的好处主要体现在以下几个方面:

代码重用:

抽象类允许我们定义一组通用的方法和属性,这些方法和属性可以被多个派生类共享。通过继承抽象类,派生类可以自动获得这些通用方法和属性的实现,从而避免了在每个派生类中重复编写相同的代码。

设计灵活性:

抽象类为设计提供了更大的灵活性。通过将某些方法或属性声明为抽象成员(即没有具体实现的方法或属性),我们可以强制派生类提供这些成员的具体实现。这有助于确保派生类遵循一定的接口或协议,从而实现更加一致和可预测的行为。

接口定义:

抽象类可以作为接口的一种实现方式。与纯接口相比,抽象类可以包含部分实现,使得某些通用的功能可以在抽象类中实现,而派生类只需关注特定于它们自己的实现。

类型安全:

通过抽象类,我们可以限制哪些类可以作为派生类。只有实现了抽象类中所有抽象成员的类才能被实例化。这有助于确保类型安全,防止不正确的类继承和使用。

易于扩展和维护:

当需要添加新的功能或修改现有功能时,我们只需在抽象类中进行相应的更改,所有继承该抽象类的派生类都将自动获得这些更改。这大大简化了代码的扩展和维护过程。

符合面向对象的设计原则:

使用抽象类有助于遵循面向对象的设计原则,如开闭原则(对扩展开放,对修改封闭)和里氏替换原则(子类必须能够替换其基类)。这些原则有助于构建更加健壮、可维护和可扩展的软件系统。

综上所述,

使用抽象类可以提高代码的重用性、设计灵活性、类型安全性以及易于扩展和维护性,同时也有助于遵循面向对象的设计原则。

派生类中调用基类成员

在派生类中调用基类成员的方法取决于成员的类型(字段、属性、方法)以及成员的访问修饰符。以下是几种常见的方式来在派生类中调用基类成员:

通过base关键字调用:

当派生类需要访问或调用被覆盖(override)的基类方法或属性时,可以使用base关键字来引用基类中的成员。
base关键字用于访问基类的构造函数、字段、属性、方法等。

public class BaseClass  
{  
    public void BaseMethod()  
    {  
        Console.WriteLine("This is the base method.");  
    }  
}  
  
public class DerivedClass : BaseClass  
{  
    public new void BaseMethod() // 注意这里使用了new关键字来隐藏基类方法  
    {  
        base.BaseMethod(); // 调用基类中被覆盖的方法  
        Console.WriteLine("This is the derived method.");  
    }  
}

直接调用:

如果派生类没有覆盖基类的成员,那么可以直接调用这些成员,而无需使用base关键字。

public class DerivedClass : BaseClass  
{  
    public void CallBaseMethod()  
    {  
        BaseMethod(); // 直接调用基类方法  
    }  
}

通过构造函数:

在派生类的构造函数中,可以使用base关键字来调用基类的构造函数。

public class BaseClass  
{  
    public BaseClass()  
    {  
        Console.WriteLine("BaseClass constructor called.");  
    }  
}  
  
public class DerivedClass : BaseClass  
{  
    public DerivedClass() : base() // 调用基类的构造函数  
    {  
        Console.WriteLine("DerivedClass constructor called.");  
    }  
}

请注意,如果基类成员是私有的(private),那么派生类无法直接访问它们。私有成员只能在定义它们的类内部被访问。如果基类成员是受保护的(protected),则派生类可以直接访问这些成员。如果基类成员是公共的(public)或受保护的,那么派生类可以像访问任何其他公共或受保护成员一样访问它们。

此外,当派生类使用new关键字隐藏基类中的成员时,虽然派生类成员和基类成员具有相同的名称,但它们实际上是两个不同的成员。在这种情况下,如果需要调用基类中被隐藏的成员,就必须使用base关键字。

在C#中,new关键字有多个用途

它们在不同上下文中具有不同的意义。以下是new关键字在C#中的一些主要用途:

对象实例化:

最常见的用法是创建对象实例。例如:

MyClass myObject = new MyClass();

这里,new MyClass()用于调用MyClass的构造函数并创建该类的一个新实例。

隐藏基类成员:

当在派生类中定义与基类成员同名的成员时,可以使用new关键字来显式地表示派生类成员隐藏了基类成员。这样做通常是为了避免基类成员的意外调用。

class Base  
{  
    public void MyMethod() { /* ... */ }  
}  
 
class Derived : Base  
{  
    public new void MyMethod() { /* ... */ }  
}

在这个例子中,Derived类中的MyMethod隐藏了Base类中的MyMethod

约束:

在泛型类、接口或方法的定义中,new()约束用于指定类型参数必须具有无参数的公共构造函数。

public class MyClass<T> where T : new()  
{  
    public T CreateInstance()  
    {  
        return new T();  
    }  
}

这里,new()约束确保T类型有一个无参数的构造函数,因此可以在CreateInstance方法中创建它的实例。

运算符重载:

new运算符也可以被重载以用于自定义类型。这通常与资源管理和释放相关,尤其是在实现IDisposable接口时。但是,直接重载new运算符并不常见,因为C#提供了更具体的语法来管理资源(如using语句)。

初始化数组:

new用于创建数组实例,并可以初始化数组元素。

int[] myArray = new int[5] { 1, 2, 3, 4, 5 };

这里,new int[5]创建一个包含5个整数的数组,并通过花括号中的值进行初始化。

随对象一起初始化字段:

在对象实例化时,可以使用对象初始化器语法与new一起初始化对象的字段或属性。

var person = new Person  
{  
    Name = "Alice",  
    Age = 30  
};

这里,new Person创建了一个Person对象,并使用对象初始化器语法设置了NameAge属性。

请注意new关键字的具体行为取决于它在哪里以及如何被使用。在使用new时,应确保你了解其当前上下文中的确切意义,以避免潜在的错误或混淆。

例子

using System;

class TODO {
    static void Main() {
        Console.Write("Hello C#!\n");

        Tools tools = new Tools();
        int ans = tools.add(1, 2);
        Console.WriteLine(ans);

        Cars chaos = new Cars();
        Console.WriteLine("this thing is {0}.", chaos.SayName());
        chaos.x = 1;
        chaos.y = 2;
        chaos.z = 3;
        Console.WriteLine(chaos.Area());
    }
}

class Tools {
    public int add(int x, int y) { return x + y; }
    public int add(int x, int y, int z) { return x + y + z; }
}

abstract class Boxs {
    public int x;
    public int y;
    public int z;
    public int Area() {
        return x * y * x;
    }

    abstract public string SayName();
    virtual public void SayHello() { System.Console.Write("Hello!\n"); }

    private int total;
}

class Cars: Boxs {
    
    public override string SayName() { return "CAR";  }
    public override void SayHello() {
        base.SayHello();
    }
    public new int Area() {
        return x + y + z;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AcWing 2816. 判断子序列(双指针)

—>原题链接 思路: 1.首先定义两个指针 i 和 j 分别指向x和y的起始位置 2.开始循环遍历x和y数组,如果 x[i] y[j] 那么i,否则j,遍历到最后in那么就说明x是y的子序列 图解 上代码: #include <iostream> using namespace std;const int N 111111;int n,m,x[N],y[N]…

脱敏技术!!!

什么是数据脱敏&#xff1f;&#xff1f;&#xff1f; 数据脱敏&#xff08;Data Masking&#xff09;是一种数据安全技术&#xff0c;旨在通过预先设定的规则和算法&#xff0c;对原始数据中包含的敏感信息进行变形处理&#xff0c;使得这些信息在非生产环境&#xff08;例如…

OpenHarmony实战开发-从0到1实现购物应用页面

概述 OpenHarmony ArkUI框架提供了丰富的动画组件和接口&#xff0c;开发者可以根据实际场景和开发需求&#xff0c;选用丰富的动画组件和接口来实现不同的动画效果。 本Codelab中&#xff0c;我们会构建一个简易的购物应用。应用包含两级页面&#xff0c;分别是主页&#xf…

【正点原子FreeRTOS学习笔记】————(7)任务调度

这里写目录标题 一、开启任务调度器&#xff08;熟悉&#xff09;二、启动第一个任务&#xff08;熟悉&#xff09;2.1&#xff0c;prvStartFirstTask () /* 开启第一个任务 */2.2&#xff0c;vPortSVCHandler () /* SVC中断服务函数 */ 三、任务切换&#xff08;掌握&#xff…

【元胞自动机】MATLAB界面聚合的元胞自动机模拟完整实现运行

文末有完整代码分享链接 文件介绍 automain 为元胞自动机主函数 choosedirection 选择方向函数&#xff0c;主函数调用 judgedirection 判断位置函数&#xff0c;主函数调用 neighbor 求每个元胞的邻居函数&#xff0c;主函数调用 surfaceness 求表面粗糙度 porosity 求孔隙率…

开源AI引擎|信息抽取与文本分类项目案例:提升12345政务投诉处理效率

一、实际案例介绍 采集员案件上报流程是城市管理和问题解决的关键环节&#xff0c;涉及对案件类别的选择、案件来源的记录、详细案件描述的填写以及现场图片的上传。这一流程要求采集员准确、详细地提供案件信息&#xff0c;以便系统能够自动解析关键数据并填写相关内容&#…

Python读取PDF文字转txt,解决分栏识别问题,能读两栏

搜索了一下&#xff0c;大致有这些库能将PDF转txt 1. PyPDF/PyPDF2&#xff08;截止2024.03.28这两个已经合并成了一个&#xff09;pypdf PyPI 2. pdfplumber GitHub - jsvine/pdfplumber: Plumb a PDF for detailed information about each char, rectangle, line, et cete…

VSCode 如何同步显示网页在手机或者平板上

首先要确保 ①电脑上安装了VsCode ②VsCode安装插件LiveServer 安装成功之后 连续按住 Alt L 、Alt O 会跳转到对应的html页面上 http://127.0.0.1:5500/....... 是这个开头的 然后打开网络 如果桌面有网上邻居的可以直接点桌面的网上邻居 进来找到WLAN这个…

spark核心概念

DAG 所谓DAG就是有向无环图&#xff0c;其实就是个无环的流程&#xff0c;Spark的核心是根据RDD来实现的&#xff0c;Spark Scheduler!则为Spark核心实现的重要一环&#xff0c;其作用就是任务调度。Spark的任务调度就是如何组织任务去处理RDD中每个分区的数据&#xff0c;根据…

AI智能分析网关V4如何使用GB28181注册到EasyCVR平台?具体步骤是什么?

旭帆科技的智能分析网关V4内含近40种智能分析算法&#xff0c;包括人体、车辆、消防、环境卫生、异常检测等等&#xff0c;在消防安全、生产安全、行为检测等场景应用十分广泛。如常见的智慧工地、智慧校园、智慧景区、智慧城管等等&#xff0c;还支持抓拍、记录、告警、语音对…

JavaScript基础练习题之计算数组元素的和与平均值

一、如何使用JavaScript计算数组元素的和与平均值&#xff1f; 二、正确的源程序 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>计算数组元素的和与平均值</title></head><body><h1>计算数组元…

HarmonyOS 应用开发之AbilityStage组件容器

AbilityStage是一个 Module 级别的组件容器&#xff0c;应用的HAP在首次加载时会创建一个AbilityStage实例&#xff0c;可以对该Module进行初始化等操作。 AbilityStage与Module一一对应&#xff0c;即一个Module拥有一个AbilityStage。 DevEco Studio默认工程中未自动生成Ab…

Linux常见指令解析一

Linux常见指令解析一 常见指令1. ls 指令2.pwd 命令3.cd 命令4.touch 命令5.mkdir 命令6.rmdir指令 && rm 指令7.man 指令8.cp 指令9.cat 命令 && tac 命令10.mv 指令11.more 指令12.less 指令13.head 指令14.tail 指令15.cal 指令 常见指令 1. ls 指令 语法…

包含具有多种类型信息的3D模型

1982年&#xff0c;Gbor Bojr开始使用类似于1975年的建筑描述系统技术来开发建筑信息软件。随后&#xff0c;1984年Bojr发布了用于Apple Lisa操作系统的Graphisoft的Radar CH。该软件技术被称为ArchiCAD于1987年重新推出&#xff0c;这是第一个能够在个人计算机上运行的建筑信息…

【源码分析】一文看透集合容器

一文看透集合容器 一、Mapa. HashMapb.ConcurrentHashMapc.HashTabled. TreeMap 二、Collectiona. ListArrayListLinkedListVectorCopyOnWriteArrayList对比和自身思考思考&#xff1a;为什么都拒绝使用Vector啊&#xff1f;它线程安全诶 b. SetHashSetTreeSetCopyOnWriteArray…

2024年【烟花爆竹产品涉药】免费试题及烟花爆竹产品涉药考试技巧

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【烟花爆竹产品涉药】免费试题及烟花爆竹产品涉药考试技巧&#xff0c;包含烟花爆竹产品涉药免费试题答案和解析及烟花爆竹产品涉药考试技巧练习。安全生产模拟考试一点通结合国家烟花爆竹产品涉药考试最新大纲…

135.分发糖果

javapublic class Solution {public int candy(int[] ratings) {// 获取孩子人数int len ratings.length;// 初始化一个数组存储每个孩子的糖果数&#xff0c;默认第一个孩子有1颗糖果int[] candyVec new int[len];candyVec[0] 1;// 阶段1&#xff1a;从左到右遍历for (int …

MongoDB内存过高问题分析解决

告警 公司有个3.2.7版本的mongo复制集&#xff0c;最近几天频繁告警内存过高。 服务器配置16C64G内存。mongo备节点内存使用到55G&#xff0c;触发告警。 以下内容基于3.2.7版本&#xff0c;3.2.7版本已经太老&#xff0c;很多后来的命令和配置&#xff0c;3.2.7都没有。 …

C++自主点餐系统

一、 题目 设计一个自助点餐系统&#xff0c;方便顾客自己点餐&#xff0c;并提供对餐厅销售情况的统计和管理功能。 二、 业务流程图 三、 系统功能结构图 四、 类的设计 五、 程序代码与说明 头文件1. SystemMap.h #pragma once #ifndef SYSTEMMAP #define SYSTEMMAP #in…

vue3全局引入element-plus使用Message教程

文章目录 安装引入 Element Plus和组件样式示例注意安装与引入&#xff1a;按需引入&#xff1a;API 使用&#xff1a;样式问题&#xff1a;组件上下文&#xff1a;版本兼容性&#xff1a;错误处理&#xff1a; 这是 Element UI 的 Vue 3 版本。ElMessage 是 Element Plus 中的…
最新文章