C#之(Virtual)虚拟与(abstract)抽象

C#之(Virtual)虚拟与(abstract)抽象

在上篇我们讲解了override(重写)与overload(重载),里面涉及到了虚拟方法和抽象方法,所以我们现在重点把(Virtual)虚拟与(abstract)抽象,在这里给大家详细讲解一下
override(重写)与overload(重载)详解

一,(Virtual)虚拟

  • 在 C# 语言中,virtual是虚拟的含义,默认情况下类中的成员都是非虚拟的.
  • 我们将类中的成员定义成虚拟的,表示这些成员将会在继承后重写其中的内容。
  • virtual 关键字能修饰方法、属性、索引器以及事件等,运用在父类的成员中。

virtual 的语法

virtual关键字修饰属性
//修饰属性
public  virtual  数据类型  属性名{get; set; }
virtual关键字修饰方法
//修饰方法
访问修饰符  virtual  返回值类型 方法名
{
    语句块;
}

virtual的案例

  • (虚拟方法)在父类中创建虚拟(virtual)方法
class A
    {
        //虚拟方法;关键字virtual
        public virtual void dog()//虚拟方法
        {
            Console.WriteLine("父类中的虚拟方法:我叫哈士奇");
        }
        public virtual void cat()//虚拟方法
        {
            Console.WriteLine("父类中的虚拟方法:我叫小猫咪");
        }
        public void eat()//普通方法
        {
            Console.WriteLine("我是哈爷,我爱吃空气");
        }
    }
  • 虚拟方法)在子类继承父类之后,我们对父类中的虚拟方法进行重写(override)
  • 重写以后子类就会将父类中的虚拟方法进行覆盖,最后的结果不会显示父类中的方法,只会显示我们在子类重写以后的方法
  • 除非我们输入(base.[父类中的方法名称])父类的方法就不会被覆盖,依旧可以显示
 class B:A
    {
        public override void dog()//重写的虚拟方法
        {
            base.dog();//重写以后如果想调用父类中的方法则输入base.[父类中的方法名称]
            Console.WriteLine("子类中重写以后的方法:我改名了,我叫哈爷");
        }
        public override void cat()//重写的虚拟方法
        {
            base.cat();//重写以后如果想调用父类中的方法则输入base.[父类中的方法名称]
            Console.WriteLine("子类中重写以后的方法:我也改名了,我叫瞄崽");
        }
    }
  • (虚拟方法)在主程序入口实例化子类进行调用
class Program
    {
        static void Main(string[] args)
        {
            B cx = new B();//实例化子类
            //调用虚拟方法
            cx.dog();
            cx.cat();
            cx.eat();
            Console.ReadLine();
        }
    }
  • 虚拟方法)没有输入(base.[父类中的方法名称])的显示结果
子类中重写以后的方法:我改名了,我叫哈爷
子类中重写以后的方法:我也改名了,我叫瞄崽
我是哈爷,我爱吃空气
  • (虚拟方法)输入(base.[父类中的方法名称])的显示结果
父类中的虚拟方法:我叫哈士奇
子类中重写以后的方法:我改名了,我叫哈爷
父类中的虚拟方法:我叫小猫咪
子类中重写以后的方法:我也改名了,我叫瞄崽
我是哈爷,我爱吃空气

注意事项;

  • virtual 关键字不能修饰使用 static 修饰的成员。
  • virtual 关键字既可以添加到访问修饰符的后面,也可以添加到访问修饰符的前面,但实际应用中习惯将该关键字放到访问修饰符的后面。

二,(abstract)抽象

  • C# abstract 关键字代表的是抽象的,使用该关键字能修饰类和方法,修饰的方法被称为抽象方法、修饰的类被称为抽象类

abstract语法

在 C# 语言中抽象方法是一种不带方法体的方法,仅包含方法的定义,语法形式如下。
  • 其中,当 abstract 用于修饰方法时,也可以将 abstract 放到访问修饰符的前面。
  • 抽象方法定义后面的“;”符号是必须保留的。需要注意的是,抽象方法必须定义在抽象类中。
访问修饰符  abstract  方法返回值类型  方法名(参数列表);
在定义抽象类时,若使用 abstract 修饰类,将其放到 class 关键字的前面,语法形式如下。
  • 其中“abstract”关键字也可以放到访问修饰符的前面。
访问修饰符  abstract class  类名
{
    //类成员
}

abstract的案例

  • (抽象方法)在父类中创建抽象(abstract)方法
  • 一旦一个方法被声明为抽象方法(abstract)则该方法不能有主体(具体的实现)
  • 而且该方法所对应的类,必须是抽象的类
 abstract class  A
    {
        //抽象方法;关键字abstract
        public abstract void Junk();//抽象方法
        public abstract void He();//抽象方法

    }
  • 抽象方法)在子类继承父类之后,我们对父类中的抽象方法进行重写(override)
  • 一个子类继承了一个抽象类,那么子类必须重写父类中所有的抽象方法
class B:A
    {
        public override void Junk()
        {
            Console.WriteLine("子类中重写的抽象方法我喜欢喝可乐");
        }

        public override void He()
        {
            Console.WriteLine("子类中重写的抽象方法:我喜欢喝雪碧");
        }
    }
  • (抽象方法)在主程序入口实例化子类进行调用
class Program
    {
        static void Main(string[] args)
        {
            B cx = new B();//实例化子类
            //调用抽象方法
            cx.Junk();
            cx.He();
            Console.ReadLine();
        }
    }
  • (抽象方法)最后的显示结果为
子类中重写的抽象方法我喜欢喝可乐
子类中重写的抽象方法:我喜欢喝雪碧

注意事项;

  • 在抽象类中可以定义抽象方法,也可以定义非抽象方法。
  • 通常抽象类会被其他类继承,并重写其中的抽象方法或者虚方法。
  • 此外,尽管在抽象类中仍然能定义构造器,但抽象类不能实例化,即不能使用如下语句。
new  抽象类的名称();
  • 在实际应用中,子类仅能重写父类中的虚方法或者抽象方法,当不需要使用父类中方法的内容时,将其定义成抽象方法,否则将方法定义成虚方法。

(Virtual)虚拟方法与(abstract)抽象方法的区别?

虚拟方法和抽象方法都可以供派生类重写和都用override重写。那么它们之间有什么区别呢?

  1. 抽象方法使用abstract关键字,虚拟方法使用virtual关键字

  2. 抽象方法是可以看成是没有实现体的虚拟方法

  3. 虚拟方法与多态性关系密切,虚拟方法为子类提供了重写该方法的选项允许子类完全或部分重写该类的方法,必须写方法体。

    抽象方法只是一个定义,没有提供实现部分,也就是没有{},也不要在里面写内容,需要在子类中实现,抽象方法是一种强制子类重写的方法,否则子类将不能被实例化。

  4. 抽象方法必须在派生类中重写,这一点跟接口类似,虚拟方法不必。

  5. 抽象方法不能声明方法体,而虚拟方法可以。

  6. 抽象类不能被实例化(不可以new),只能实例化实现了全部抽象方法的派生类;而包含虚方法的类可以实例化。

  7. 虚拟方法是指能被重载覆盖的方法,而抽象方法是虚拟方法中的特例,指完全没有具体实现的虚拟方法.

  8. 如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其它一般方法

  9. 抽象方法是必须被派生类覆写的方法,调用虚拟方法,运行时将确定调用对象是什么类的实例,并调用适当的覆写的方法

  10. .一个虚拟方法的实现可以由派生类取代。取代所继承的虚拟方法的实现的过程称为重写该方法;在一个虚拟方法调用中,该调用所涉及的那个实例的运行时类型确定了要被调用的究竟是该方法的哪一个实现。

  11. 抽象方法是需要子类去实现的.虚方法,是已经实现了,子类可以去覆盖,也可以不覆盖取决于需求.

    看过以后有什么问题,请及时留言,本人会及时改正

    感觉还不错的点波关注,以后会陆续更新新的疑难杂症.

  • 12
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基本信息 书名:高等学校计算机应用规划教材:Visual C# 2010程序设计教程 作者: 陈建伟 出版社: 清华大学出版社 页码:326 出版日期:2012-06-01 内容简介   《高等学校计算机应用规划教材:Visual C# 2010程序设计教程》详细介绍了Visual C# 2010程序设计的基础知识、基本方法和应用技巧,共分14章,主要内容包括.NET平台与Visual Studio 2010开发环境、C#语言基础及面向对象程序设计、C#程序设计、C# Web程序设计、C#泛型编程、C#数据库程序设计及ADO.NET等相关知识,并在最后讲解了运用C#处理文件和进行LINQ语言集成查询的内容。   本书的讲述由浅入深、循序渐进,并针对各章知识点附以大量的示例程序和习题。通过本书的学习,读者可以逐步掌握C#程序设计。   本书难度适中,实例丰富,可操作性强,可作为高等学校计算机相关专业的教材或参考用书,也可供广大程序员参考。 目录 第1章.NET平台与Visual Studio开发工具 1.1 Microsoft.NET平台 1.1.1 .NET Framework 4.0概述 1.1.2 理解命名空间 1.2 Visual Studio 2010简介 1.2.1 Visual Studio 2010开发环境概览 1.2.2 菜单栏 1.2.3 工具栏 1.2.4 “属性”及“解决方案资源管理器”面板 1.2.5 其他面板 1.2.6 Visual Studio 2010的新特性 1.3 创建控制台应用程序 1.4 本章小结 1.5 习题 第2章 Visual C# 2010语法基础 2.1 C#语言概述 2.2 C#基础元素 2.2.1 语句 2.2.2 标识符与关键字 2.3 变量 2.3.1 变量的命名 2.3.2 变量的声明和赋值 2.4 数据类型 2.4.1 简单类型 2.4.2 结构类型 2.4.3 枚举类型 2.4.4 引用类型 2.4.5 装箱与拆箱 2.4.6 数据类型的转换 2.5 运算符与表达式 2.5.1 赋值运算符与表达式 2.5.2 关系运算符与表达式 2.5.3 逻辑运算符与表达式 2.5.4 其他运算符与表达式 2.5.5 运算符的优先级 2.6 Visual C# 2010的新特性 2.6.1 大整数类型(Biginteger) 2.6.2 动态数据类型 2.6.3 命名参数和可选参数 2.7 本章小结 2.8 上机练习 2.9 习题 第3章 程序流程控制 3.1 选择结构程序设计 3.1.1 if语句 3.1.2 switch语句 3.2 循环结构程序设计 3.2.1 for语句 3.2.2 for each语句 3.2.3 while语句 3.2.4 do---while语句 3.2.5 跳出循环 3.3 异常处理结构 3.3.1 异常的产生 3.3.2 处理异常 3.4 本章小结 3.5 上机练习 3.6 习题 第4章 数组与集合 4.1 数组 4.1.1 数组的声明 4.1.2 -维数组的使用 4.1.3 多维数组的使用 4.2 集合 4.2.1 集合的定义 4.2.2 集合的使用 4.2.3 常用系统预定义的集合类 4.3 本章小结 4.4 上机练习 4.5 习题 第5章 C#面向对象程序设计基础 5.1 面向对象程序设计概述 5.2 类与对象 5.2.1 类与对象概述 5.2.2 面向对象程序设计相关概念 5.2.3 类的声明与System Object类 5.2.4 对象的声明与类的实例化 5.2.5 类成员 5.2.6 类成员的访问限制 5.2.7 this关键字 5.3 构造函数与析构函数 5.3.1 构造函数 5.3.2 析构函数 5.4 本章小结 5.5 上机练习 5.6 习题 第6章 域、属性与事件 6.1 域 6.1.1 域的初始化 6.1.2 只读域与readonly关键字 6.2 属性 6.2.1 属性的声明 6.2.2 属性的访问 6.3 事件 6.3.1 委托 6.3.2 事件的声明 6.3.3 事件的订阅与取消 6.4 本章小结 6.5 上机练习 6.6 习题 第7章 方法 7.1 方法的声明 7.2 方法的参数 7.2.1 值类型参数传递 7.2.2 引用类型参数传递 7.2.3 输出类型参数传递 7.2.4 数组类型参数传递 7.3 静态方法 7.4 方法的重载 7.5 外部方法 7.6 操作符重载 7.6.1 一元操作符的重载 7.6.2 二元操作符的重载 7.7 本章小结 7.8 上机练习 7.9 习题 第8章 继承与多态 8.1 什么是继承 8.2 使用继承机制 8.2.1 基类和派生类 8.2.2 bas
两个现实中的例子: 1、B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的生理情况。这是如何做到的呢?B超是B型超声波,它可以透过肚皮通过向你体内发射B型超声波,当超声波遇到内脏壁的时候就会产生一定的“回音”反射,然后把“回音”进行处理就可以显示出内脏的情况了(我不是医生也不是声学专家,不知说得是否准确^_^)。 2、地球内部结构:地球的内部结构大体可以分为三层:地壳、地幔和地核。地壳是固体,地核是液体,地幔则是半液半固的结构(中学地理的内容,大家还记得吧?)。如何在地球表面不用深入地球内部就知道其内部的构造呢?对,向地球发射“地震波”,“地震波”分两种一种是“横波”,另一种是“纵波”。“横波”只能穿透固体,而“纵波”既可穿透固体又可以穿透液体。通过在地面对纵波和横波的反回情况,我们就可以大体断定地球内部的构造了。 大家注意到这两个例子的共同特点,就是从一个对象的外部去了解对象内部的构造,而且都是利用了波的反射功能。在.NET中的反射也可以实现从对象的外部来了解对象(或程序集)内部结构的功能,哪怕你不知道这个对象(或程序集)是个什么东西,另外.NET中的反射还可以运态创建出对象并执行它其中的方法。 反射是.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。有了反射,即可对每一个类型了如指掌。另外我还可以直接创建对象,即使这个对象的类型在编译时还不知道。 反射的用途: (1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。 (2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。 (3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstractvirtual)等。 (4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstractvirtual)等。 (5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。 (6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。 (7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。 (8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。 反射用到的命名空间: System.Reflection System.Type System.Reflection.Assembly 反射用到的主要类: System.Type 类--通过这个类可以访问任何给定数据类型的信息。 System.Reflection.Assembly类--它可以用于访问给定程序集的信息,或者把这个程序集加载到程序中。 System.Type类: System.Type 类对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。 获取给定类型的Type引用有3种常用方式: ●使用 C# typeof 运算符。 Type t = typeof(string); ●使用对象GetType()方法。 string s = "grayworm"; Type t = s.GetType(); ●还可以调用Type类的静态方法GetType()。 Type t = Type.GetType("System.String"); 上面这三类代码都是获取string类型的Type,在取出string类型的Type引用t后,我们就可以通过t来探测string类型的结构了。 string n = "grayworm"; Type t = n.GetType(); foreach (MemberInfo mi in t.GetMembers()) { Console.WriteLine("{0}/t{1}",mi.MemberType,mi.Name); } Type类的属性: Name 数据类型名 FullName 数据类型的完全限定名(包括命名空间名) Namespace 定义数据类型的命名空间名 IsAbstract 指示该类型是否是抽象类型 IsArray 指示该类型是否是数组 IsClass 指示该类型是否是类 IsEnum 指示该类型是否是枚举 IsInterface 指示该类型是否是接口 IsPublic 指示该类型是否是公有的 IsSealed 指示该类型是否是密封类 IsValueType 指示该类型是否是值类型 Type类的方法: GetConstructor(), GetConstructors():返回ConstructorInfo类型,用于取得该类的构造函数的信息 GetEvent(), GetEvents():返回EventInfo类型,用于取得该类的事件的信息 GetField(), GetFields():返回FieldInfo类型,用于取得该类的字段(成员变量)的信息 GetInterface(), GetInterfaces():返回InterfaceInfo类型,用于取得该类实现的接口的信息 GetMember(), GetMembers():返回MemberInfo类型,用于取得该类的所有成员的信息 GetMethod(), GetMethods():返回MethodInfo类型,用于取得该类的方法的信息 GetProperty(), GetProperties():返回PropertyInfo类型,用于取得该类的属性的信息 可以调用这些成员,其方式是调用Type的InvokeMember()方法,或者调用MethodInfo, PropertyInfo和其他类的Invoke()方法。 查看类中的构造方法: NewClassw nc = new NewClassw(); Type t = nc.GetType(); ConstructorInfo[] ci = t.GetConstructors(); //获取类的所有构造函数 foreach (ConstructorInfo c in ci) //遍历每一个构造函数 { ParameterInfo[] ps = c.GetParameters(); //取出每个构造函数的所有参数 foreach (ParameterInfo pi in ps) //遍历并打印所该构造函数的所有参数 { Console.Write(pi.ParameterType.ToString()+" "+pi.Name+","); } Console.WriteLine(); }
C#中的虚方法和抽象方法都是实现多态性的重要手段,它们的区别在于抽象方法必须在派生类中被重写并提供具体的实现,而虚方法则可以在派生类中被重写也可以不被重写。下面是两者的具体介绍: 1. 虚方法(Virtual Methods): 虚方法是指在基类中声明并提供默认实现的方法,它可以在派生类中被重写。通过使用关键字"virtual",可以标记一个方法为虚方法。虚方法在基类中可以包含方法体,也可以不包含方法体。如果在派生类中重写了虚方法,那么在派生类对象上调用该方法时,实际调用的是派生类中的重写方法。 下面是一个虚方法的例子: ```csharp class Shape { public virtual void Draw() { Console.WriteLine("Drawing a shape"); } } class Circle : Shape { public override void Draw() { Console.WriteLine("Drawing a circle"); } } class Square : Shape { // 不重写基类中的虚方法 } class Program { static void Main(string[] args) { Shape s1 = new Circle(); Shape s2 = new Square(); s1.Draw(); // 输出:Drawing a circle s2.Draw(); // 输出:Drawing a shape } } ``` 2. 抽象方法(Abstract Methods): 抽象方法是指在基类中声明但没有提供实现的方法。抽象方法必须在派生类中被重写并提供具体的实现。通过使用关键字"abstract",可以标记一个方法为抽象方法。抽象方法在基类中只能进行声明,不能包含方法体。 下面是一个抽象方法的例子: ```csharp abstract class Shape { public abstract void Draw(); } class Circle : Shape { public override void Draw() { Console.WriteLine("Drawing a circle"); } } class Square : Shape { public override void Draw() { Console.WriteLine("Drawing a square"); } } class Program { static void Main(string[] args) { Shape s1 = new Circle(); Shape s2 = new Square(); s1.Draw(); // 输出:Drawing a circle s2.Draw(); // 输出:Drawing a square } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值