重写与方法隐藏

发布于 2024-09-25 13:03:25 字数 556 浏览 3 评论 0原文

我对 C# 中的重写和隐藏方法有点困惑。每个的实际用途以及对何时使用每个的解释也将受到赞赏。

我对覆盖感到困惑 - 为什么我们要覆盖?到目前为止我了解到的是,通过覆盖,我们可以为派生类的方法提供所需的实现,而无需更改签名。

如果我不重写超类的方法并且对子类中的方法进行更改,那么是否会对超类方法进行更改?

我也对以下内容感到困惑 - 这表明了什么?

class A
{
    virtual m1()
    {
        console.writeline("Bye to all");
    }
}

class B : A
{
    override m1()
    {
        console.writeLine("Hi to all");
    }
}

class C
{
    A a = new A();
    B b = new B();
    a = b; (what is this)
    a.m1(); // what this will print and why?

    b = a; // what happens here?
}

I am a bit confused about overriding vs. hiding a method in C#. Practical uses of each would also be appreciated, as well as an explanation for when one would use each.

I am confused about overriding - why do we override? What I have learnt so far is that by overring we can provide desired implementation to a method of a derived class, without changing the signature.

If I don't override the method of the superclass and I make changes to the method in the sub class, will that make changes to the super class method ?

I am also confused about the following - what does this demonstrate?

class A
{
    virtual m1()
    {
        console.writeline("Bye to all");
    }
}

class B : A
{
    override m1()
    {
        console.writeLine("Hi to all");
    }
}

class C
{
    A a = new A();
    B b = new B();
    a = b; (what is this)
    a.m1(); // what this will print and why?

    b = a; // what happens here?
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

滥情稳全场 2024-10-02 13:03:25

考虑一下:

public class BaseClass
{
  public void WriteNum()
  {
    Console.WriteLine(12);
  }
  public virtual void WriteStr()
  {
    Console.WriteLine("abc");
  }
}

public class DerivedClass : BaseClass
{
  public new void WriteNum()
  {
    Console.WriteLine(42);
  }
  public override void WriteStr()
  {
    Console.WriteLine("xyz");
  }
}
/* ... */
BaseClass isReallyBase = new BaseClass();
BaseClass isReallyDerived = new DerivedClass();
DerivedClass isClearlyDerived = new DerivedClass();

isReallyBase.WriteNum(); // writes 12
isReallyBase.WriteStr(); // writes abc
isReallyDerived.WriteNum(); // writes 12
isReallyDerived.WriteStr(); // writes xyz
isClearlyDerived.WriteNum(); // writes 42
isClearlyDerived.writeStr(); // writes xyz

重写是经典的面向对象方式,派生类可以比基类具有更具体的行为(在某些语言中,您别无选择,只能这样做)。当对对象调用虚拟方法时,将调用该方法的最派生版本。因此,即使我们将 isReallyDerived 作为 BaseClass 处理,也会使用 DerivedClass 中定义的功能。

隐藏意味着我们有完全不同的方法。当我们在 isReallyDerived 上调用 WriteNum() 时,我们无法知道 DerivedClass 上有不同的 WriteNum() code> 所以它没有被调用。仅当我们将对象作为 DerivedClass 处理时才能调用它。

大多数时候隐藏是不好的。一般来说,如果一个方法可能在派生类中更改,则应该将其设置为虚拟方法,然后在派生类中重写它。然而,它有两个用途:

  1. 向前兼容性。如果 DerivedClass 有一个 DoStuff() 方法,然后将 BaseClass 更改为添加一个 DoStuff()方法,(请记住,它们可能由不同的人编写并存在于不同的程序集中),那么对成员隐藏的禁止将突然使 DerivedClass 出现错误,而无需更改。此外,如果 BaseClass 上的新 DoStuff() 是虚拟的,那么在 DerivedClass 上自动对其进行重写可能会导致预编译现有方法在不应该调用的时候被调用。因此,最好将隐藏设置为默认值(我们使用 new 来明确表示我们确实要隐藏,但如果不隐藏它,则会隐藏并在编译时发出警告)。

  2. 穷人的协方差。考虑 BaseClass 上的 Clone() 方法,该方法返回一个新的 BaseClass,它是所创建的 BaseClass 的副本。在 DerivedClass 的重写中,这将创建一个 DerivedClass 但将其作为 BaseClass 返回,这没有那么有用。我们可以做的是重写一个虚拟的受保护的CreateClone()。在 BaseClass 中,我们有一个 Clone(),它返回结果 - 一切都很好 - 在 DerivedClass 中,我们用一个新的 < code>Clone() 返回一个 DerivedClass。在 BaseClass 上调用 Clone() 将始终返回一个 BaseClass 引用,该引用将是一个 BaseClass 值或 < code>DerivedClass 适当的值。对 DerivedClass 调用 Clone() 将返回一个 DerivedClass 值,这正是我们在该上下文中想要的值。此原理还有其他变体,但应该注意的是,它们都非常罕见。

对于第二种情况,需要注意的一个重要事项是,我们精确地使用隐藏来消除调用代码的意外,因为使用 DerivedClass 的人可能合理地期望其 < code>Clone() 返回一个 DerivedClass。任何调用方式的结果都保持一致。大多数隐藏的情况都会带来意外的风险,这就是为什么它们通常不受欢迎的原因。正是因为它解决了隐藏经常带来的问题,所以这一点是合理的。

总而言之,隐藏有时是必要的,虽然很少有用,但通常是不好的,所以要非常警惕。

Consider:

public class BaseClass
{
  public void WriteNum()
  {
    Console.WriteLine(12);
  }
  public virtual void WriteStr()
  {
    Console.WriteLine("abc");
  }
}

public class DerivedClass : BaseClass
{
  public new void WriteNum()
  {
    Console.WriteLine(42);
  }
  public override void WriteStr()
  {
    Console.WriteLine("xyz");
  }
}
/* ... */
BaseClass isReallyBase = new BaseClass();
BaseClass isReallyDerived = new DerivedClass();
DerivedClass isClearlyDerived = new DerivedClass();

isReallyBase.WriteNum(); // writes 12
isReallyBase.WriteStr(); // writes abc
isReallyDerived.WriteNum(); // writes 12
isReallyDerived.WriteStr(); // writes xyz
isClearlyDerived.WriteNum(); // writes 42
isClearlyDerived.writeStr(); // writes xyz

Overriding is the classic OO way in which a derived class can have more specific behaviour than a base class (in some languages you've no choice but to do so). When a virtual method is called on an object, then the most derived version of the method is called. Hence even though we are dealing with isReallyDerived as a BaseClass then functionality defined in DerivedClass is used.

Hiding means that we have a completely different method. When we call WriteNum() on isReallyDerived then there's no way of knowing that there is a different WriteNum() on DerivedClass so it isn't called. It can only be called when we are dealing with the object as a DerivedClass.

Most of the time hiding is bad. Generally, either you should have a method as virtual if its likely to be changed in a derived class, and override it in the derived class. There are however two things it is useful for:

  1. Forward compatibility. If DerivedClass had a DoStuff() method, and then later on BaseClass was changed to add a DoStuff() method, (remember that they may be written by different people and exist in different assemblies) then a ban on member hiding would have suddenly made DerivedClass buggy without it changing. Also, if the new DoStuff() on BaseClass was virtual, then automatically making that on DerivedClass an override of it could lead to the pre-existing method being called when it shouldn't. Hence it's good that hiding is the default (we use new to make it clear we definitely want to hide, but leaving it out hides and emits a warning on compilation).

  2. Poor-man's covariance. Consider a Clone() method on BaseClass that returns a new BaseClass that's a copy of that created. In the override on DerivedClass this will create a DerivedClass but return it as a BaseClass, which isn't as useful. What we could do is to have a virtual protected CreateClone() that is overridden. In BaseClass we have a Clone() that returns the result of this - and all is well - in DerivedClass we hide this with a new Clone() that returns a DerivedClass. Calling Clone() on BaseClass will always return a BaseClass reference, which will be a BaseClass value or a DerivedClass value as appropriate. Calling Clone() on DerivedClass will return a DerivedClass value, which is what we'd want in that context. There are other variants of this principle, however it should be noted that they are all pretty rare.

An important thing to note with the second case, is that we've used hiding precisely to remove surprises to the calling code, as the person using DerivedClass might reasonably expect its Clone() to return a DerivedClass. The results of any of the ways it could be called are kept consistent with each other. Most cases of hiding risk introducing surprises, which is why they are generally frowned upon. This one is justified precisely because it solves the very problem that hiding often introduces.

In all, hiding is sometimes necessary, infrequently useful, but generally bad, so be very wary of it.

燃情 2024-10-02 13:03:25

重写是指您在子类中提供方法的新重写实现,而该方法在基类中定义为虚拟

隐藏是指您在子类中提供方法的新实现,而该方法在基类中定义为虚拟,或者您的新实现未指定覆盖

隐藏往往是不好的;如果可以避免的话,通常应该尽量不这样做。隐藏可能会导致意外的事情发生,因为隐藏方法仅在您定义的实际类型的变量上调用时使用,而不是在使用基类引用时使用......另一方面,被重写的虚拟方法最终将是调用正确的方法版本,即使使用子类上的基类引用进行调用也是如此。

例如,考虑这些类:

public class BaseClass
{
  public virtual void Method1()  //Virtual method
  {
    Console.WriteLine("Running BaseClass Method1");
  }
  public void Method2()  //Not a virtual method
  {
    Console.WriteLine("Running BaseClass Method2");
  }
}
public class InheritedClass : BaseClass
{
  public override void Method1()  //Overriding the base virtual method.
  {
    Console.WriteLine("Running InheritedClass Method1");
  }
  public new void Method2()  //Can't override the base method; must 'new' it.
  {
    Console.WriteLine("Running InheritedClass Method2");
  }
}

让我们在匹配的引用中使用 InheritedClass 的实例这样调用它

InheritedClass inherited = new InheritedClass();
inherited.Method1();
inherited.Method2();

:这两种方法都表示它们正在运行 InheritedClass 版本。

运行 InheritedClass 方法 1
运行InheritedClass方法2

此代码创建相同 InheritedClass 的实例,但将其存储在 BaseClass 引用中:

BaseClass baseRef = new InheritedClass();
baseRef.Method1();
baseRef.Method2();

通常,根据 OOP 原则,您应该期望得到与上面示例相同的输出。但你没有得到相同的输出:

运行 InheritedClass 方法 1
运行基类方法2

当您编写 InheritedClass 代码时,您可能希望对 Method2() 的所有调用都运行您在其中编写的代码。通常,这就是它的工作方式 - 假设您正在使用已重写的虚拟方法。但由于您使用的是 new/hidden 方法,因此它会调用您正在使用的引用上的版本。


如果这是您真正想要的行为,那么;就这样吧。但我强烈建议,如果这就是您想要的,那么代码可能存在更大的架构问题。

Overriding is when you provide a new override implementation of a method in a descendant class when that method is defined in the base class as virtual.

Hiding is when you provide a new implementation of a method in a descendant class when that method is not defined in the base class as virtual, or when your new implementation does not specify override.

Hiding is very often bad; you should generally try not to do it if you can avoid it at all. Hiding can cause unexpected things to happen, because Hidden methods are only used when called on a variable of the actual type you defined, not if using a base class reference... on the other hand, Virtual methods which are overridden will end up with the proper method version being called, even when called using the base class reference on a child class.

For instance, consider these classes:

public class BaseClass
{
  public virtual void Method1()  //Virtual method
  {
    Console.WriteLine("Running BaseClass Method1");
  }
  public void Method2()  //Not a virtual method
  {
    Console.WriteLine("Running BaseClass Method2");
  }
}
public class InheritedClass : BaseClass
{
  public override void Method1()  //Overriding the base virtual method.
  {
    Console.WriteLine("Running InheritedClass Method1");
  }
  public new void Method2()  //Can't override the base method; must 'new' it.
  {
    Console.WriteLine("Running InheritedClass Method2");
  }
}

Let's call it like this, with an instance of InheritedClass, in a matching reference:

InheritedClass inherited = new InheritedClass();
inherited.Method1();
inherited.Method2();

This returns what you should expect; both methods say they are running the InheritedClass versions.

Running InheritedClass Method1
Running InheritedClass Method2

This code creates an instance of the same, InheritedClass, but stores it in a BaseClass reference:

BaseClass baseRef = new InheritedClass();
baseRef.Method1();
baseRef.Method2();

Normally, under OOP principles, you should expect the same output as the above example. But you don't get the same output:

Running InheritedClass Method1
Running BaseClass Method2

When you wrote the InheritedClass code, you may have wanted all calls to Method2() to run the code you wrote in it. Normally, this would be how it works - assuming you are working with a virtual method that you have overridden. But because you are using a new/hidden method, it calls the version on the reference you are using, instead.


If that's the behavior you truly want, then; there you go. But I would strongly suggest that if that's what you want, there may be a larger architectural issue with the code.

野生奥特曼 2024-10-02 13:03:25

方法重写是简单地重写派生类中基类方法的默认实现。

方法隐藏:您可以在派生类中的虚拟方法之前使用“new”关键字,

就像

class Foo  
{  
  public virtual void foo1()  
  {  

  }  
}  

class Bar:Foo  
{  
  public new virtual void foo1()  
  {   

  }  
}  

现在如果您创建另一个从 Bar 派生的类 Bar1 一样,您可以覆盖 Bar 中定义的 foo1 。

Method Overriding is simpley override a default implementation of a base class method in the derived class.

Method Hiding : You can make use of 'new' keyword before a virtual method in a derived class

as

class Foo  
{  
  public virtual void foo1()  
  {  

  }  
}  

class Bar:Foo  
{  
  public new virtual void foo1()  
  {   

  }  
}  

now if you make another class Bar1 which is derived from Bar , you can override foo1 which is defind in Bar.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文