abstract virtaul override new 及多态

abstract 

abstract 修饰符可以和类、方法、属性、索引器及事件一起使用。在类声明中使用 abstract 修饰符以指示某个类只能是其他类的基类。标记为抽象或包含在抽象类中的成员必须通过从抽象类派生的类来实现。

抽象类具有以下特性:

  • 抽象类不能实例化。
  • 抽象类可以包含抽象方法和抽象访问器。
  • 不能用 sealed(C# 参考) 修饰符修改抽象类,因为 sealed 会使得抽象类无法被继承。
  • 从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实实现。

抽象方法具有以下特性:

  • 抽象方法是隐式的虚方法。
  • 只允许在抽象类中使用抽象方法声明。(即抽象方法只能在抽象类中)
  • 因为抽象方法声明不提供实际的实现,所以没有方法体;方法声明只是以一个分号结束,并且在签名后没有大括号 ({ })。
    • 例如: public abstract void MyMethod();
  • 实现由一个重写方法override(C# 参考)提供,此重写方法是非抽象类的一个成员。
  • 在抽象方法声明中使用 static 或 virtual 修饰符是错误的。

除了在声明和调用语法上不同外,抽象属性的行为与抽象方法一样。

  • 在静态属性上使用 abstract 修饰符是错误的。
  • 在派生类中,通过包括使用 override 修饰符的属性声明,可以重写抽象的继承属性。

virtaul 

virtaul 关键字用于修饰方法、属性、索引器或事件声明,并使它们可以在派生类中被重写(为了被重写abstract 及virtaul 都不能是私有的)

调用虚方法时,将为重写成员检查该对象的运行时类型。将调用大部分派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员。

默认情况下,方法是非虚拟的。不能重写非虚方法。

virtual 修饰符不能与 static、abstract, private 或 override 修饰符一起使用。

除了声明和调用语法不同外,虚拟属性的行为与抽象方法一样。

  • 在静态属性上使用 virtual 修饰符是错误的。
  • 通过包括使用 override 修饰符的属性声明,可在派生类中重写虚拟继承属性。

派生类

在 C# 中,派生类可以包含与基类方法同名的方法。

  • 基类方法必须定义为 virtual
  • 如果派生类中的方法前面没有 new 或 override 关键字,则编译器将发出警告,该方法将有如存在 new 关键字一样执行操作。
  • 如果派生类中的方法前面带有 new 关键字,则该方法被定义为独立于基类中的方法。(使用 new 关键字可告诉编译器您的定义将隐藏基类中包含的定义。这是默认行为。)
  • 如果派生类中的方法前面带有 override 关键字,则派生类的对象将调用该方法,而不是调用基类方法。
  • 可以从派生类中使用 base 关键字调用基类方法。
  • override、virtual 和 new 关键字还可以用于属性、索引器和事件中。

在 C# 中,派生类中方法的名称可与基类中方法的名称相同。可通过使用 new 和 override 关键字指定方法互动的方式。override 修饰符 extends 基类方法,且 new 修饰符将其“隐藏”起来。

New关键字主要用来区别派生类和基类同名方法的选择问题,通过隐藏基类方法,达到使编译器调用正确的方法的目的。Override主要用来对基类的方  法和虚方法进行重写。(如果A基类中有虚方法a,那派生类B,C分别用override及new重写a,若B,C在实例化时使用的类型是A定义的,那使用调用a时发布是B中方法,A中方法,若B,C在实例化时使用的类型其本身派生类的类型定义的,那使用调用a时发布是B中方法,C中方法)

如果希望派生成员具有与基类中的成员相同的名称,但又不希望派生成员参与虚调用,则可以使用 new 关键字。new 关键字放置在要替换的类成员的返回类型之前

使用新成员隐藏基类成员(其实就是new与override的区别,从文字上来说一个是隐藏一个是重写)

如果希望派生成员具有与基类中的成员相同的名称,但又不希望派生成员参与虚调用,则可以使用 new 关键字。new 关键字放置在要替换的类成员的返回类型之前。以下代码提供了一个示例:

C#

public class BaseClass
{
    public void DoWork() { WorkField++; }
    public int WorkField;
    public int WorkProperty
    {
        get { return 0; }
    }
}

public class DerivedClass : BaseClass
{
    public new void DoWork() { WorkField++; }
    public new int WorkField;
    public new int WorkProperty
    {
        get { return 0; }
    }
}

通过将派生类的实例强制转换为基类的实例,仍然可以从客户端代码访问隐藏的基类成员。例如:

C#

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.

从派生类访问基类虚拟成员

已替换或重写某个方法或属性的派生类仍然可以使用基关键字访问基类的该方法或属性。 以下代码提供了一个示例:

C#

public class Base
{
    public virtual void DoWork() {/*...*/ }
}
public class Derived : Base
{
    public override void DoWork()
    {
        //Perform Derived's work here //... // Call DoWork on base class base.DoWork();
    }
}

重写和方法选择

当在类中指定方法时,如果有多个方法与调用兼容(例如,存在两种同名的方法,并且其参数与传递的参数兼容),则 C# 编译器将选择最佳方法进行调用。下面的方法将是兼容的:

publicclass Derived : Base
{
    publicoverridevoid DoWork(int param) { }
    publicvoid DoWork(double param) { }
}

 

A.DoWork();  // Calls the old method.

阻止派生类重写虚拟成员

无论在虚拟成员和最初声明虚拟成员的类之间已声明了多少个类,虚拟成员永远都是虚拟的。如果类 A 声明了一个虚拟成员,类 B 从 A 派生,类 C 从类 B 派生,则类 C 继承该虚拟成员,并且可以选择重写它,而不管类 B 是否为该成员声明了重写。以下代码提供了一个示例:

public class A
{
    public virtual void DoWork() { }
}
public class B : A
{
    public override void DoWork() { }
}

派生类可以通过将重写声明为 sealed 来停止虚拟继承。 这需要在类成员声明中的 override 关键字前面放置 sealed 关键字。以下代码提供了一个示例:

public class C : B
{
    public sealed override void DoWork() { }
}

在上一个示例中,方法 DoWork 对从 C 派生的任何类都不再是虚拟方法。即使它们转换为类型 B 或类型 A,它对于 C 的实例仍然是虚拟的。通过使用 new 关键字,密封的方法可以由派生类替换,如下面的示例所示:

public class D : C
{
    public new void DoWork() { }
}

在此情况下,如果在 D 中使用类型为 D 的变量调用 DoWork,被调用的将是新的 DoWork。如果使用类型为 C、B 或 A 的变量访问 D 的实例,对 DoWork 的调用将遵循虚拟继承的规则,即把这些调用传送到类 C 的 DoWork 实现。

将 virtual 方法声明为 abstract

// compile with: /target:librarypublicclass D
{
    public virtual void DoWork(int i)
    {
        // Original implementation.
    }
}

public abstract class E : D
{
    public abstract override void DoWork(int i);
}

publicclass F : E
{
    public override void DoWork(int i)
    {
        // New implementation.
    }
}

如果将 virtual 方法声明为 abstract,则该方法对于从抽象类继承的所有类而言仍然是虚方法。继承抽象方法的类无法访问该方法的原始实现。在前面的示例中,类 F 上的 DoWork 无法调用类 D 上的 DoWork。在此情况下,抽象类可以强制派生类为虚方法提供新的方法实现。 

密封类和类成员

通过在类定义前面放置关键字 sealed,可以将类声明为密封类。例如:

public sealed class D
{
    // Class members here.
}

密封类不能用作基类。因此,它也不能是抽象类。密封类禁止派生。由于密封类从不用作基类,所以有些运行时优化可以使对密封类成员的调用略快。

在对基类的虚成员进行重写的派生类上的类成员、方法、字段、属性或事件可以将该成员声明为密封成员。在用于以后的派生类时,这将取消成员的虚效果。方法是在类成员声明中将 sealed 关键字置于 override 关键字的前面。例如:

多态  (上面的都是铺垫)

多态性常被视为自封装和继承之后,面向对象的编程的第三个支柱。

  • 在运行时,在方法参数和集合或数组等位置,派生类的对象可以作为基类的对象处理。发生此情况时,该对象的声明类型不再与运行时类型相同。
  • 基类可以定义并实现方法,派生类可以重写这些方法,即派生类提供自己的定义和实现。在运行时,客户端代码调用该方法,CLR 查找对象的运行时类型,并调用虚方法的重写方法。因此,你可以在源代码中调用基类的方法,但执行该方法的派生类版本。 虚方法/抽象方法/接口都是可以实现多态的(因为MSDN上的例子是用抽象方法写的,所以网上一些人说只有抽象方法才能实现多态,这一点是错误的,特别说明下) 直接看代码
class Program

    {

        interface IdoWork

        {

            void Do();

        }

 

        abstract class DoWork  //若使用IdoWork会有一样的结果

        {

            public abstract void Do();

        }

 

        class DoMyWork

        {

            public virtual void Do()

            {

                Console.WriteLine("DoMyWork");

            }

        }

 

        class Do1:DoWork

        {

 

            public override void Do()

            {

                Console.WriteLine("Do1");

            }

        }

 

        class Do2 : DoWork

        {

 

            public override void Do()

            {

                Console.WriteLine("Do2");

            }

        }

 

        class MyDo1 : DoMyWork

        {

 

            public override void Do()

            {

                Console.WriteLine("MyDo1");

            }

        }

 

        class MyDo2 : DoMyWork

        {

 

            public override void Do()

            {

                Console.WriteLine("MyDo2");

            }

        }

 

        class MyDo3 : DoMyWork

        {

 

            public new void Do()

            {

                Console.WriteLine("MyDo3");

            }

        }

 

        static void Main(string[] args)

        {

 

            contact ct1 = new class1();

 

            contact ct2 = new class2();

 

            class2 ct3 = new class2();

 

            ct1.prinf();

 

            ct2.prinf();

 

            ct3.prinf();

 

            Console.ReadKey();

 

            List<DoWork> Dos = new List<DoWork>();

            Dos.Add(new Do1());

            Dos.Add(new Do2());

            Dos[0].Do();

            Dos[1].Do();

            Console.ReadKey();

 

            List<DoMyWork> MyDos = new List<DoMyWork>();

            MyDos.Add(new MyDo1());

            MyDos.Add(new MyDo2());

            MyDos.Add(new MyDo3());

            MyDos[0].Do();

            MyDos[1].Do();

            MyDos[2].Do();

            Console.ReadKey();

        }

 

    }

 

    abstract public class contact

    {

 

        public virtual void prinf()

        {

 

            Console.WriteLine("这是虚方法");

 

        }

 

    }

 

    public class class1 : contact

    {

 

        public override void prinf()

        {

 

            Console.WriteLine("这是新的方法");

 

        }

 

    }

 

    public class class2 : contact

    {

 

        public new void prinf()

        {

 

            Console.WriteLine("这是另一个新的方法");

 

        }

 

    }

 

}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏C语言C++游戏编程

有人@我,你有一份C语言基础大全手册要领取,快来拿!

前两天,有网友问了我一个关于C语言的问题,本着认真装逼的态度,我把大学时学过的C语言课本翻了一遍,终于找到了答案。整理后,现分享给大家!

1232
来自专栏xingoo, 一个梦想做发明家的程序员

【面试虐菜】—— JAVA面试题(1)

今天参加笔试,里面有设计模式,和一些基础题! 印象最深的是:什么不是Object的函数,我蒙的finalize,哎,无知! 还问了,接口与抽象类的不同,还...

2059
来自专栏CSDN技术头条

在下函数式编程,有何贵干?

本文简单介绍了一下函数式编程的各种基本特性,希望能够对于准备使用函数式编程的人起到一定入门作用。 ? 函数式编程,一个一直以来都酷,很酷,非常酷的名词。虽然诞生...

1977
来自专栏精讲JAVA

Comparable 与 Comparator 浅析

来源:朱小厮, blog.csdn.net/u013256816/article/details/50899416 今天博主在翻阅TreeMap的源码,发现其键...

2256
来自专栏Micro_awake web

JavaScript实现八大内部排序算法

? 注:基数排序中:r是关键字的基数,d是长度,n是关键字的个数 1.插入排序 基本思想:在序号i之前的元素(0到i-1)已经排好序,本趟需要找到i对应的元素...

2419
来自专栏我和PYTHON有个约会

32.企业级开发进阶4:正则表达式

本节内容,要讲解的和我们的信息检索有关系,这一方面也是Python在目前非常流行的一个应用方向:爬虫。

991
来自专栏青玉伏案

窥探Swift之别样的枚举类型

  想必写过程序的童鞋对枚举类型并不陌生吧,使用枚举类型的好处是多多的,在这儿就不做过多的赘述了。Fundation框架和UIKit中的枚举更是数不胜数,枚举可...

2027
来自专栏老九学堂

【必读】超全的C语言基础知识大全

我们用一个简单的c程序例子,介绍c语言的基本构成、格式、以及良好的书写风格,加深小伙伴们对C语言的认识。

2112
来自专栏平凡文摘

Comparable 与 Comparator 浅析

1174
来自专栏C/C++基础

C++11 变参模板

版权声明:感谢您对博文的关注!校招与社招,有需要内推腾讯的可以QQ(1589276509)or 微信(louislvlv)联系我哈,期待您的加入。 ...

2942

扫码关注云+社区

领取腾讯云代金券