为什么我不能像在方法上那样对类变量使用 virtual/override?

发布于 2024-08-24 13:47:04 字数 1901 浏览 6 评论 0原文

在下面的示例中,我能够在继承类中创建一个虚拟方法Show(),然后覆盖它位于继承类中。

我想对受保护的类变量 前缀同样的事情,但出现错误:

修饰符“virtual”无效 对于这个项目

但是由于我无法在我的类中将此变量定义为虚拟/覆盖,因此我收到编译器警告

TestOverride234355.SecondaryTransaction.prefix' 隐藏继承的成员 'TestOverride234355.Transaction.prefix'。 如果需要隐藏,请使用 new 关键字 有意为之。

幸运的是,当我添加 new 关键字时,一切都工作正常,这很好,因为我获得了相同的功能,但这引发了两个问题

  1. 为什么我可以对方法使用 virtual/override,但不能对受保护的类变量使用 virtual/override?

  2. 虚拟/覆盖方法和 hide-it-with-new 方法之间实际上有什么区别,因为至少在这个示例中它们提供了相同的功能?

代码:

using System;

namespace TestOverride234355
{
    public class Program
    {
        static void Main(string[] args)
        {
            Transaction st1 = new Transaction { Name = "name1", State = "state1" };
            SecondaryTransaction st2 = 
                new SecondaryTransaction { Name = "name1", State = "state1" };

            Console.WriteLine(st1.Show());
            Console.WriteLine(st2.Show());

            Console.ReadLine();
        }
    }

    public class Transaction
    {
        public string Name { get; set; }
        public string State { get; set; }

        protected string prefix = "Primary";

        public virtual string Show()
        {
            return String.Format("{0}: {1}, {2}", prefix, Name, State);
        }
    }

    public class SecondaryTransaction : Transaction
    {
        protected new string prefix = "Secondary";

        public override string Show()
        {
            return String.Format("{0}: {1}, {2}", prefix, Name, State);
        }
    }
}

In the following example I am able to create a virtual method Show() in the inherited class and then override it in the inheriting class.

I want to do the same thing with the protected class variable prefix but I get the error:

The modifier 'virtual' is not valid
for this item

But since I can't define this variable as virtual/override in my classes, I get the compiler warning:

TestOverride234355.SecondaryTransaction.prefix'
hides inherited member
'TestOverride234355.Transaction.prefix'.
Use the new keyword if hiding was
intended.

Luckily when I add the new keyword everything works fine, which is ok since I get the same functionality, but this raises two questions:

  1. Why I can use virtual/override for methods but not for protected class variables?

  2. What is the difference actually between the virtual/override approach and the hide-it-with-new approach since at least in this example they offer the same functionality?

Code:

using System;

namespace TestOverride234355
{
    public class Program
    {
        static void Main(string[] args)
        {
            Transaction st1 = new Transaction { Name = "name1", State = "state1" };
            SecondaryTransaction st2 = 
                new SecondaryTransaction { Name = "name1", State = "state1" };

            Console.WriteLine(st1.Show());
            Console.WriteLine(st2.Show());

            Console.ReadLine();
        }
    }

    public class Transaction
    {
        public string Name { get; set; }
        public string State { get; set; }

        protected string prefix = "Primary";

        public virtual string Show()
        {
            return String.Format("{0}: {1}, {2}", prefix, Name, State);
        }
    }

    public class SecondaryTransaction : Transaction
    {
        protected new string prefix = "Secondary";

        public override string Show()
        {
            return String.Format("{0}: {1}, {2}", prefix, Name, State);
        }
    }
}

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

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

发布评论

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

评论(7

贵在坚持 2024-08-31 13:47:04

覆盖字段并没有真正的意义。它是基类状态的一部分,如果继承类希望更改它,则应该通过为其提供适当的可见性来在继承类中更改它。

在您的情况下,您可以做的一件事是在继承类的构造函数中设置前缀:

// Base class field declaration and constructor
protected string prefix;

public Transaction()
{
  prefix = "Primary";
}

// Child class constructor
public SecondaryTransaction()
{
  prefix = "Secondary";
}

您还可以创建属性而不是字段,并将属性设置为虚拟。这将使您能够更改继承类中属性的 getter 和 setter 的行为:

// Base class
public virtual string Prefix { get { /* ... */ } set { /* ... */ } }

// Child class
public override string Prefix { get { /* ... */ } set { /* ... */ } }

编辑: 至于在继承类设置变量之前在基构造函数中使用变量的问题,解决这个问题的一种方法是在基类中定义一个初始化方法,在继承类中重写它,并在访问任何字段之前从基构造函数中调用它:

// Base class
public class Base
{
  protected string prefix;

  public Base()
  {
    Initialize();
    Console.WriteLine(prefix);
  }  

  protected virtual void Initialize()
  {
    prefix = "Primary";
  }
}

// Inheriting class
public class Child : Base
{
  public override void Initialize()
  {
    prefix = "Secondary";
  }
}

编辑2:您还问了虚拟/覆盖和名称隐藏(方法上的新关键字)之间的区别在于,是否应该避免它,以及它是否有用。

名称隐藏是一种在隐藏虚拟方法的情况下破坏继承的功能。即,如果您在子类中隐藏 Initialize() 方法,基类将看不到它,也不会调用它。此外,如果 Initialize() 方法是公共的,则对基本类型的引用调用 Initialize() 的外部代码将调用 Initialize()< /code> 基本类型。

当基类中的方法是非虚拟的,并且子类想要提供自己的不同实现时,名称隐藏非常有用。但请注意,这与虚拟/覆盖不同。基类型的引用将调用基类型的实现,子类型的引用将调用子类型的实现。

Overriding a field does not really make sense. It's part of the state of the base class, and if an inheriting class wishes to change it, it should be changable in the inheriting class by giving it an appropriate visibility.

One thing you could do in your case is to set prefix in the constructor for the inheriting class:

// Base class field declaration and constructor
protected string prefix;

public Transaction()
{
  prefix = "Primary";
}

// Child class constructor
public SecondaryTransaction()
{
  prefix = "Secondary";
}

You can also make a property instead of a field, and make the property virtual. This will enable you to change the behavior of the getter and setter for the property in the inheriting class:

// Base class
public virtual string Prefix { get { /* ... */ } set { /* ... */ } }

// Child class
public override string Prefix { get { /* ... */ } set { /* ... */ } }

EDIT: As for your question of using a variable in a base constructor before an inheriting class has set it, one way to solve this is to define an initialization method in the base class, override it in the inheriting class, and call it from the base constructor before accessing any fields:

// Base class
public class Base
{
  protected string prefix;

  public Base()
  {
    Initialize();
    Console.WriteLine(prefix);
  }  

  protected virtual void Initialize()
  {
    prefix = "Primary";
  }
}

// Inheriting class
public class Child : Base
{
  public override void Initialize()
  {
    prefix = "Secondary";
  }
}

EDIT 2: You also asked what the difference between virtual/override and name hiding (the new keyword on methods) is, if it should be avoided, and if it can be useful.

Name hiding is a feature that breaks inheritance in the case of hiding virtual methods. I.e., if you hide the Initialize() method in the child class, the base class will not see it, and not call it. Also, if the Initialize() method was public, external code that was calling Initialize() on a reference of the base type would be calling Initialize() on the base type.

Name hiding is useful when a method is non-virtual in a base class, and a child wants to provide a different implementation of its own. Note, however, that this is NOT the same as virtual/override. References of the base type will call the base type implementation, and references of the child type will call the child type implementation.

洋洋洒洒 2024-08-31 13:47:04

静态或非虚拟方法或属性只是一个内存地址(为了简化问题)。虚拟方法或属性由表中的条目标识。该表取决于定义方法或属性的类。
当您重写派生类中的虚拟成员时,您实际上更改了派生类表中的条目以指向重写方法。
在运行时,对此类成员的访问始终通过表进行。因此该条目可以被任何派生类覆盖。

字段没有这样的机制,因为它们应该被快速访问。

在成员上使用“新”意味着您不想覆盖表中的条目,而是想要一个新成员(与现有虚拟成员同名,如果您问我,这是一种不好的做法)。

如果通过指向基类的指针访问虚拟成员,则永远不会访问派生类中定义为“新”的成员,这是问题第二部分中提到的差异。

A static or non-virtual method or property is just a memory address (to simplify things). A virtual method or property is identified by an entry in a table. This table is dependent on the class defining the method or property.
When you override a virtual member in a derived class, you actually change the entry in the table for the derived class to point to the overriding method.
At run-time, access to such a member goes though the table, always. So the entry can be overridden by any derived class.

There is no such mechanism for fields, as they're meant to be accessed quickly.

Using 'new' on a member means that you do not want to override the entry in the table, but that you want a new member (with the same name as an existing virtual one, a bad practice if you ask me).

If you access a virtual member through a pointer to the base class, you'll never access the member defined as 'new' in the derived class, which is the difference mentioned in the second part of your question.

仅一夜美梦 2024-08-31 13:47:04

而是为前缀成员创建一个属性 - 这样您可以将属性设置为 virtual/abstract

字段用于存储对象的状态,它们帮助对象封装数据并隐藏其他人的实现问题。通过能够覆盖字段,我们将类的实现问题泄漏给客户端代码(包括子类型)。因此,大多数语言都决定不能定义可以被覆盖的实例变量(尽管它们可以是公共/受保护的......因此您可以访问它们)。

您也不能将实例变量放入接口中

Rather create a property for the prefix member - this way you can set the property to virtual/abstract

Fields are used to store state for an object, they help the object encapsulate data and hide implementation concerns from others. By being able to override a field we are leaking the implementation concerns of the class to client code (including subtypes). Due to this most languages have taken the decision that one cannot define instance variables that can be overridden (although they can be public/protected... so you can access them).

You also cannot put instance variables in an interface

-黛色若梦 2024-08-31 13:47:04

在您的示例中,如果您没有重写SecondaryTransaction类中的“Show”,那么在SecondaryTransaction的实例上调用Show实际上会调用基类(Transaction)中的方法,因此将在基类中使用“Show”类,导致输出:

主要:名称1,状态1 
主要:名称 1、状态 1

因此,根据您调用的方法(即基类或子类上的方法),代码将具有不同的“前缀”值,这将是可维护性的噩梦。我怀疑您可能想要/应该做的是在包含“前缀”的事务上公开一个属性。

您无法重写字段,因为它是基类的实现细节。您可以更改受保护字段的,但通过覆盖它,您实际上是在说我想要替换字段,而不是 >。

我会做什么(如果我绝对不想/无法使用属性):

public class Transaction
{
    public string Name { get; set; }
    public string State { get; set; }
    protected string prefix = "Primary";
    public virtual string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}
public class SecondaryTransaction : Transaction
{ 
    public SecondaryTransaction()
    {
        prefix = "Secondary";
    }
    public override string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}

编辑:(根据我对另一个答案的评论)

如果您是向下调用基类的 ctor 并需要设置值,那么您可能必须修改 Transaction,可能如下所示:

public class Transaction
{
    public string Name { get; set; }
    public string State { get; set; }
    protected string prefix = "Primary";
    // Declared as virtual ratther than abstract to avoid having to implement "TransactionBase"
    protected virtual void Initialise()
    { }
    public Transaction()
    {
        Initialise();
    }
    public virtual string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}
public class SecondaryTransaction : Transaction
{ 
    protected override void Initialise()
    {
        prefix = "Secondary";
    }
    public override string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}

In your example, if you didn't override "Show" in the SecondaryTransaction class, then calling Show on an instance of SecondaryTransaction would actually be calling the method in the base class (Transaction), which would therefore use "Show" in the base class, resulting in output of:

Primary: name1, state1 
Primary: name1, state1

So, depending on what method you were calling (i.e. one on the base class or the child class), the code would have a different value for "prefix" which would be a maintainability nightmare. I suspect what you probably want to/should do, is expose a property on Transaction that wraps "prefix".

You can't override a field because it's an implementation detail of the base class. You can change the value of a protected field, but by overriding it you'd essentially be saying I want to replace the field, not the value.

What I would do (if I absolutely didn't want to/couldn't use properties) :

public class Transaction
{
    public string Name { get; set; }
    public string State { get; set; }
    protected string prefix = "Primary";
    public virtual string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}
public class SecondaryTransaction : Transaction
{ 
    public SecondaryTransaction()
    {
        prefix = "Secondary";
    }
    public override string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}

Edit: (As per my comment on another answer)

If you're calling down into your base class's ctor and need the value set, then you'll probably have to modify Transaction, possibly like this:

public class Transaction
{
    public string Name { get; set; }
    public string State { get; set; }
    protected string prefix = "Primary";
    // Declared as virtual ratther than abstract to avoid having to implement "TransactionBase"
    protected virtual void Initialise()
    { }
    public Transaction()
    {
        Initialise();
    }
    public virtual string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}
public class SecondaryTransaction : Transaction
{ 
    protected override void Initialise()
    {
        prefix = "Secondary";
    }
    public override string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}
乖乖兔^ω^ 2024-08-31 13:47:04

为什么要覆盖受保护的变量,当然您想要做的就是将其设置为覆盖类中的其他内容(可能在构造函数中)?

Why do you want to override a protected variable, surely all you want to do is set it to something else in the overriding class (possibly in the constructor)?

故事灯 2024-08-31 13:47:04

覆盖字段是无稽之谈。将字段标记为受保护,您可以自动在派生类中访问它们。您可以覆盖函数、属性,因为它在内部使用函数。

Overriding a field is a nonsense. Marking field as protected you automatically may access them in derived classes. You may override functions, properties, because it uses functions internally.

拧巴小姐 2024-08-31 13:47:04

你不能,因为没有用。通过覆盖一个字段你会完成什么?
简单:

class Animal{

    public class Head {
        int eye = 8;
    } 
    Head head = new Head()
    public void Test() 
    { 
        print head.eye; 
        DoOtherStufs(); 
    } //8

    protected virtual void DoOtherStufs() 
    {}
}

class Cat : Animal{
    class new Head : Animal.Head 
    {
        int mouth;
    } 
    Head head = new Head()
    protected override void DoOtherStufs() 
    { 
        print head.eye; 
    }
} 
Cat cat = new Cat();

cat.head.eye = 9;
print cat.Test() 

这将打印 8 9 而不是预期的 9 9。
我需要一个基类函数,但我还需要一个继承类,我可以操纵(增加)内部组类变量内的变量。这是不可能的!

You can't because there is no use. What would you accomplish by overriding a field?
Simple:

class Animal{

    public class Head {
        int eye = 8;
    } 
    Head head = new Head()
    public void Test() 
    { 
        print head.eye; 
        DoOtherStufs(); 
    } //8

    protected virtual void DoOtherStufs() 
    {}
}

class Cat : Animal{
    class new Head : Animal.Head 
    {
        int mouth;
    } 
    Head head = new Head()
    protected override void DoOtherStufs() 
    { 
        print head.eye; 
    }
} 
Cat cat = new Cat();

cat.head.eye = 9;
print cat.Test() 

This will print 8 9 instead of 9 9 as expected.
I need a base class functional, but I also need a inherited class with I could manipulate (increase) the vars inside internal group classes vars. Its not possible!!

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