想知道以下内容之间的区别:

案例1:基类

public void DoIt();


案例1:继承的类

public new void DoIt();


案例2:基类

案例2:继承的类

public virtual void DoIt();


根据我进行的测试,案例1和案例2似乎具有相同的效果。有区别还是首选方式?

评论

与许多问题重复,包括stackoverflow.com/questions/159978/…

#1 楼


Override修饰符可以在
虚拟方法上使用,并且必须在
抽象方法上使用。这表示
编译器使用方法的最后定义的
实现。即使
在对
基类的引用上调用该方法,它也将使用
实现重写它。



如果覆盖Derived.DoIt,则会调用Base.DoIt


new修饰符指示
编译器使用子类实现
而不是父类
执行。任何不是
引用您的类而是父类
的代码都将使用父类
实现。


public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public override void DoIt()
    {
    }
}

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt


请先致电Base.DoIt,然后致电Derived.DoIt。实际上,它们是两个完全独立的方法,它们恰好具有相同的名称,而不是派生的方法覆盖基本方法。

来源:Microsoft Blog

评论


这指示编译器使用方法的最后定义的实现。如何找到方法的最后定义的实现?

– AminM
2014年5月5日19:30

从一个具体的类开始,检查它是否具有感兴趣的方法的实现。如果是这样,那么您就完成了。如果不是,请在继承层次结构中上一步,即检查超类是否具有感兴趣的方法。继续直到找到感兴趣的方法。

– csoltenborn
14-10-24在8:16

还要注意,仅当基类将方法定义为虚拟方法时,您才能覆盖方法。虚拟一词是基类,它说:“嘿,当我调用此方法时,它实际上可能已被派生的实现所替代,因此我并不真正提前知道我在运行时实际上在调用哪种方法。因此,虚拟“表示”是方法的占位符,这表示不能覆盖未标记为“虚拟”的方法,但是可以用修饰符new替换派生类中的任何非虚拟方法,该修饰符只能在派生级别访问。

–埃里克·邦格斯(Erik Bongers)
18年5月20日在10:03



#2 楼

virtual:表示方法可以被继承者覆盖

override:覆盖基类中虚拟方法的功能,提供不同的功能。

new:隐藏原始方法(不必是虚拟的),提供了不同的功能。仅应在绝对必要的情况下使用。

隐藏方法时,仍可以通过向上强制转换为基类来访问原始方法。这在某些情况下很有用,但很危险。

评论


为什么强制转换隐藏基本方法的方法很危险?或者,您是否暗示一般进行铸造很危险?

–马克
16年7月7日,下午2:39

@Mark-调用者可能不知道实现,从而导致意外的误用。

– Jon B
16年7月7日在16:26

您可以在父方法上使用覆盖和/或新方法而不使用虚拟方法吗?

–亚伦·弗兰克(Aaron Franke)
19年6月20日在4:30

#3 楼

在第一种情况下,您将定义隐藏在父类中。这意味着它仅在将对象作为子类处理时才被调用。如果将类强制转换为其父类型,则将调用父方法。在第二个实例中,该方法将被覆盖,并且无论对象是强制转换为子类还是父类,都将调用该方法。

#4 楼

尝试以下操作:(case1)

((BaseClass)(new InheritedClass())).DoIt()


编辑:virtual + override在运行时已解析(因此,override确实会覆盖虚拟方法),而new只是使用相同的方法创建新方法名称,并隐藏旧名称,它会在编译时解决->您的编译器会调用它“看到”的方法

#5 楼




new表示尊重您的REFERENCE类型(=的左侧),从而运行引用类型的方法。如果重新定义的方法没有new关键字,则其行为与原来相同。而且,它也称为非多态继承。也就是说,
“我正在派生类中创建一个全新的方法,该方法与
基类中同名的任何方法绝对无关。” -通过说Whitaker

override(必须在其基类中与virtual一起使用)意味着尊重您的OBJECT类型(=的右侧),从而
无论参考类型。此外,它也称为多态继承。




我记住两个关键字彼此相反的方式。

override:必须定义virtual关键字才能覆盖该方法。使用override关键字的方法,无论是否使用基类实例化引用类型(基类或派生类的引用),都将运行基类的方法。否则,将运行派生类的方法。

new:如果关键字被方法使用,则与override关键字不同,引用类型很重要。如果使用派生类实例化它,并且引用类型是基类,则运行基类的方法。如果使用派生类实例化它,而引用类型是派生类,则运行派生类的方法。即,是override关键字的对比。顺便说一句,如果您忘记或忽略了向该方法添加新关键字,则默认情况下,编译器的行为是使用new关键字。 />
class A 
{
    public string Foo() 
    {
        return "A";
    }

    public virtual string Test()
    {
        return "base test";
    }
}

class B: A
{
    public new string Foo() 
    {
        return "B";
    }
}

class C: B 
{
    public string Foo() 
    {
        return "C";
    }

    public override string Test() {
        return "derived test";
    }
}


输出:

A AClass = new B();
Console.WriteLine(AClass.Foo());
B BClass = new B();
Console.WriteLine(BClass.Foo());
B BClassWithC = new C();
Console.WriteLine(BClassWithC.Foo());

Console.WriteLine(AClass.Test());
Console.WriteLine(BClassWithC.Test());



新代码示例,

通过一对一注释来播放代码。

A
B
B
base test
derived test


#6 楼

在情况1中,如果您在类型被声明为基类的情况下使用了继承类的DoIt()方法,则您甚至还会看到基类的操作。


评论


您能否发布您收到的警告或错误。当我最初发布该代码时,它运行良好。

–马修·怀特(Matthew Whited)
13年2月13日在12:55

所有这些都应该粘贴到您的入口点类(程序)中。删除该内容是为了更好地格式化此网站。

–马修·怀特(Matthew Whited)
13年2月13日在12:57

#7 楼

两种情况之间的区别在于,在情况1中,基本DoIt方法不会被覆盖,而只是被隐藏。这意味着取决于变量的类型取决于将调用哪种方法。例如:

BaseClass instance1 = new SubClass();
instance1.DoIt(); // Calls base class DoIt method

SubClass instance2 = new SubClass();
instance2.DoIt(); // Calls sub class DoIt method


这可能确实令人困惑,并导致意外的行为,应尽可能避免。因此,首选方式将是情况2。

#8 楼

如果派生类中使用关键字override,则它覆盖父方法。

如果派生类中使用关键字new,则派生方法被父方法隐藏。

#9 楼

我有相同的问题,这确实令人困惑,您应该考虑覆盖和新关键字仅适用于基类类型和派生类值的对象。在这种情况下,您只会看到覆盖和新建的效果: br />
现在调用方法将考虑其状态。
重写:意味着它扩展了方法的功能,然后在派生类中使用了该方法,而new则告诉编译器将方法隐藏在派生类中,而在基类中使用该方法。
这对这个主题非常有用:

https://msdn.microsoft.com/EN -US / library / ms173153%28v = VS.140,d = hv.2%29.aspx?f = 255&MSPPError = -2147217396

#10 楼

下面的文章在vb.net中,但是我认为关于新vs覆盖的解释非常容易理解。

https://www.codeproject.com/articles/17477/the-dark-shadow -of-overrides

在本文的某些地方,有这样的句子:


通常,Shadows假定与该类型关联的功能是
会被调用,而Overrides则假定对象实现已执行。


这个问题的公认答案是完美的,但我认为本文提供了很好的示例,可以为这两个关键字之间的区别。

#11 楼

在所有这些当中,最令人困惑的是新技术。通过实验,new关键字就像为开发人员提供了通过显式定义类型来用基类实现覆盖继承类实现的选项。就像在想另一种方式。

在下面的示例中,结果将返回“派生结果”,直到将类型明确定义为BaseClass test,然后才返回“基础结果”。

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedClass();
        var result = test.DoSomething();
    }
}

class BaseClass
{
    public virtual string DoSomething()
    {
        return "Base result";
    }
}

class DerivedClass : BaseClass
{
    public new string DoSomething()
    {
        return "Derived result";
    }
}


#12 楼

在这些测试中不会显示功能上的差异:

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();


在此示例中,被称为Doit的是您希望被调用的Doit。

为了查看差异,您必须执行以下操作:

BaseClass obj = new DerivedClass();

obj.DoIt();


您将看到是否在案例1中运行了该测试(已定义)它),则调用DoIt()中的BaseClass,在情况2(如您所定义)中,则调用DoIt()中的DerivedClass

#13 楼

在第一种情况下,它将调用派生类DoIt()方法,因为new关键字隐藏了基类DoIt()方法。

在第二种情况下,它将调用重写的DoIt()

  public class A
{
    public virtual void DoIt()
    {
        Console.WriteLine("A::DoIt()");
    }
}

public class B : A
{
    new public void DoIt()
    {
        Console.WriteLine("B::DoIt()");
    }
}

public class C : A
{
    public override void DoIt()
    {
        Console.WriteLine("C::DoIt()");
    }
}


创建这些类的实例

   A instanceA = new A();

    B instanceB = new B();
    C instanceC = new C();

    instanceA.DoIt(); //A::DoIt()
    instanceB.DoIt(); //B::DoIt()
    instanceC.DoIt(); //B::DoIt()


上面的所有功能都可以预期。让将instanceB和instanceC设置为instanceA并调用DoIt()方法并检查结果。

    instanceA = instanceB;
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A

    instanceA = instanceC;
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C


评论


instanceC.DoIt();会给你C :: DoIt(),而不是B :: DoIt()

– BYS2
19年5月20日在16:12



#14 楼



none,virtual,override,new和abstract的所有组合: