C#Virtual&覆盖关键字

时间:2012-02-23 11:05:16

标签: c# asp.net .net visual-studio

我以为我已经钉了这个,然后我去看看工作中的一些来源,我想知道为什么我从msdn读到的东西和我在源头看到的东西有这么多的矛盾....

我的理解是虚拟关键字可以在方法声明中使用,以允许任何派生类覆盖它。

当实现超类的虚方法时,需要在派生类中使用override关键字....

例如:

public abstract class A
{
    public virtual string GetName();
}


public class B:A
{

    //assume there are some defined properties.
    public override string GetName()
    {
        return FirstName;
    }
}

我有几个问题:

1)如果方法没有实现,是否真的有必要将方法定义为虚拟方法?当然,它可以在子类中被覆盖而不使用虚拟和覆盖?

2)如果(1)不正确,我是否认为必须在子类中使用它来覆盖每个虚方法....

编辑:

你是对的,我的代码不会编译......我想知道为什么......我理解你的答案,但后来我看到了:

public abstract class RequestHandler<TRequest, TResponse> : RequestHandler, IRequestHandler<TRequest>, IRequestHandler, IDisposable, ITypedRequestHandler
    where TRequest : global::Agatha.Common.Request
    where TResponse : global::Agatha.Common.Response, new()
{
    protected RequestHandler();

    public virtual void AfterHandle(TRequest request);
    public virtual void BeforeHandle(TRequest request);
    public override Response CreateDefaultResponse();
    public TResponse CreateTypedResponse();
    public override Response Handle(Request request);
    public abstract Response Handle(TRequest request);
}

以上不会导致编译器抱怨...

7 个答案:

答案 0 :(得分:10)

首先上面的代码无效。虚拟方法仍然必须具有默认实现的主体。要做你上面所做的事情,你需要使用abstract键盘而不是虚拟。

abstract表示没有提供方法体,但是从中派生的任何类必须实现此方法(除非它也是抽象的)。

我认为这几乎可以回答你的问题......

  1. 如果它没有实现,那么不能是虚拟的,它必须是抽象的。如果它有一个什么都不做的实现,那么必须实现它。

  2. 虚拟类的重点是它具有默认行为,因此您可以选择是否覆盖它。如果它是抽象的那么你必须覆盖它(除非你派生另一个抽象类)。

答案 1 :(得分:2)

  

如果方法没有实现,是否真的有必要将方法定义为虚拟?

您可以将方法设为抽象(它将隐式地使其成为虚拟方法)。

  

当然可以在不使用虚拟和覆盖的情况下在子类中覆盖它?

如果你只是“覆盖”它而没有明确地覆盖它,那么将不是相同的方法,并且在基类的变量上调用该方法将不会调用派生方法(它不会参与多态)。你只是“隐藏”基类的方法(编译器实际上警告你这个,如果它真的是你想要做的,你必须使用new修饰符。)

一个例子将使它更清晰:

class B
{
    public virtual void M() { Console.WriteLine("B.M") };
}

class D1 : Base
{
    // Hides the base method
    public new void M() { Console.WriteLine("D1.M") };
}


class D2 : Base
{
    // Overrides the base method
    public override void M() { Console.WriteLine("D2.M") };
}

...

D1 d1 = new D1();
d1.M(); // Prints "D1.M"
B b1 = d1;
b1.M(); // Prints "B.M", because D1.M doesn't override B.M

D2 d2 = new D1();
d2.M(); // Prints "D2.M"
B b2 = d2;
b2.M(); // Also prints "D2.M", because D2.M overrides B.M
  

如果(1)不正确,我是否认为必须在子类中使用它来覆盖每个虚方法....

不,只有它是抽象的......虚方法可以有一个实现,在这种情况下,派生类不会被强制覆盖它。

答案 2 :(得分:1)

  

1)如果方法没有实现,是否真的有必要将方法定义为虚拟方法?当然,它可以在子类中被覆盖而不使用虚拟和覆盖?

正如其他答案所述,virtual方法需要实现。你将它与abstract混淆了。

如果您询问virtual方法是否需要声明virtual:在C#中,是的,这是必要的。在Java中,您可以覆盖任何旧方法。 C#设计决定要求使用virtual关键字特别允许覆盖,以便除非程序员有意,否则不能覆盖方法。

如果程序员没有使用virtual表达意图,你仍然可以&#34;覆盖&#34;方法,使用new关键字。但是,这有点不同。希望这段代码有助于说明这个概念:

class Program
{
    static void Main(string[] args)
    {
        var baseC = new BaseClass();
        var extC = new ExtClass();
        var lazyC = new LazyClass();

        Console.WriteLine(baseC.NewMethod());
        Console.WriteLine(baseC.VirtualOverrideMethod());

        Console.WriteLine("---");

        Console.WriteLine(extC.NewMethod());
        Console.WriteLine(extC.VirtualOverrideMethod());

        Console.WriteLine("---");

        Console.WriteLine(((BaseClass) extC).NewMethod());
        Console.WriteLine(((BaseClass) extC).VirtualOverrideMethod()); // Redundant typecast

        Console.WriteLine("---");

        Console.WriteLine(lazyC.VirtualOverrideMethod());

        Console.ReadKey();
    }

    public class BaseClass
    {
        public BaseClass()
        {

        }

        public string NewMethod()
        {
            return "NewMethod of BaseClass";
        }

        public virtual string VirtualOverrideMethod()
        {
            return "VirtualOverrideMethod of BaseClass";
        }
    }

    class ExtClass : BaseClass
    {
        public new string NewMethod()
        {
            return "NewMethod of ExtClass";
        }

        public override string VirtualOverrideMethod()
        {
            return "VirtualOverrideMethod of ExtClass";
        }
    }

    class LazyClass : BaseClass
    {
    }
}

输出:

NewMethod of BaseClass
VirtualOverrideMethod of BaseClass
---
NewMethod of ExtClass
VirtualOverrideMethod of ExtClass
---
NewMethod of BaseClass
VirtualOverrideMethod of ExtClass
---
VirtualOverrideMethod of BaseClass
  

2)如果(1)不正确,我是否认为必须在子类中使用它来覆盖每个虚方法....

完全没有。 virtual是一种说法,#34;如果你想覆盖这种方法,那就没关系了#34;。事实上,我在上面的代码中添加了LazyClass来展示这一点。

  

以上不会导致编译器抱怨...

我还没有使用过很多接口,但看起来像是一个接口。在我的代码中,如果我改变

    class ExtClass : BaseClass
    {
        public new string NewMethod()
        {
            return "NewMethod of ExtClass";
        }

        public override string VirtualOverrideMethod()
        {
            return "VirtualOverrideMethod of ExtClass";
        }
    }

为:

    class ExtClass : BaseClass
    {
        public new string NewMethod()
        {
            return "NewMethod of ExtClass";
        }

        public override string VirtualOverrideMethod()
        {
            return "VirtualOverrideMethod of ExtClass";
        }
    }

我明白了:

error CS0501: 'OverrideTest.Program.ExtClass.VirtualOverrideMethod()' must declare a body because it is not marked abstract, extern, or partial

来自Visual Studio 2010. virtual的{​​{1}}方法也是如此。

答案 3 :(得分:0)

好的,通过将方法声明为虚拟(并且不希望覆盖子类中的方法),如果要引入方法,则需要进入方法隐藏的世界。同名到子类中。你必须这样定义它:

public abstract class A
{
    public virtual string GetName()
    {
        return null;
    }
}

public class B : A
{
    //assume there are some defined properties.
    public new string GetName()
    {
        return FirstName;
    }
}

我建议您考虑抽象覆盖方法:

public abstract class A
{
    public abstract string GetName()
}

// to override
public class B : A
{
    //assume there are some defined properties.
    public override string GetName()
    {
        return FirstName;
    }
}

这将是完全不同的。我强烈建议您只重写子类中的抽象方法。

然而,这里有一个关于这个主题的快速参考(旧的,但读得很好):

http://www.akadia.com/services/dotnet_polymorphism.html

答案 4 :(得分:0)

virtual的需要由多态性决定。考虑使用new重新定义子类中的方法时会发生什么:

class Parent
{
    void Foo() { Console.WriteLine("Parent"); }
    virtual void Bar { Console.WriteLine("Parent"); }
}

class Child : Parent
{
    new void Foo() { Console.WriteLine("Child"); } // another method Foo
    override void Bar { Console.WriteLine("Child"); }
}

var child = new Child();    // a Child instance
var parent = (Parent)child; // same object, but statically typed as Parent

c.Bar(); // prints "Child"
p.Bar(); // again, prints "Child" -- expected virtual behavior

c.Foo(); // prints "Child"
p.Foo(); // but this prints "Parent"!!

出于这个原因,旨在派生的类(即不是sealed)应该始终将可以覆盖的方法标记为virtual - 否则将会出现如上所述的令人困惑的运行时行为。如果父实现实际上做了什么,那就没有区别了;重点是行为不同

答案 5 :(得分:0)

这正是virtualabstract方法之间的区别:

如果选择,派生类可以覆盖虚拟方法 。虚拟方法可能有也可能没有继承者调用的默认实现,这是使用base关键字完成的。

派生类必须覆盖抽象方法 。它的限制是:

  • 它只能在抽象类中定义。
  • 它不能有实现,因为由继承者来定义它的行为。

答案 6 :(得分:0)

  

1)如果方法没有,是否真的有必要将方法定义为虚拟方法   实施?当然它可以在子类中被覆盖   不使用虚拟和覆盖?

您可以使用没有正文的abstract方法,但如果您从该类派生,则必须才能覆盖该方法。 所有抽象方法都必须在子类中重写。

  

2)如果(1)不正确,我是否正确地认为每一个虚拟   必须在子类中使用它重写方法....

不,相反,您可以使用virtual关键字在其中设置实现,但强制在子类中进行覆盖。从这个角度来看,为您提供更大的灵活性。

通常他们使用abstract为孩子创建严格的约束,因此派生的每个子项必须实现基类的指定成员。