[英]Why can't I use virtual/override on class variables as I can on methods?
在下面的示例中,我能够在继承的类中创建一个虚方法Show()
,然后在继承类中重写它。
我想用受保护的类变量 prefix
做同样的事情但我得到错误:
修饰符'virtual'对此项无效
但由于我无法在我的类中将此变量定义为虚拟/覆盖 ,因此我收到编译器警告 :
TestOverride234355.SecondaryTransaction.prefix'隐藏继承的成员'TestOverride234355.Transaction.prefix'。 如果要隐藏,请使用new关键字。
幸运的是,当我添加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);
}
}
}
覆盖一个字段并没有多大意义。 它是基类状态的一部分,如果继承类希望改变它,那么它应该在继承类中通过赋予它适当的可见性来进行改变。
在您的情况下,您可以做的一件事是在继承类的构造函数中设置prefix
:
// 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()
。
当方法在基类中是非虚拟的时,名称隐藏很有用,并且子级想要提供自己的不同实现。 但是请注意,这是不一样的虚拟/覆盖。 基类型的引用将调用基类型实现,子类型的引用将调用子类型实现。
而是为前缀成员创建一个属性 - 这样您就可以将属性设置为virtual / abstract
字段用于存储对象的状态,它们帮助对象封装数据并隐藏其他人的实现问题。 通过能够覆盖字段,我们将类的实现问题泄露给客户端代码(包括子类型)。 由于这个原因,大多数语言都决定不能定义可以被覆盖的实例变量(虽然它们可以是公共的/受保护的......所以你可以访问它们)。
您也不能将实例变量放在接口中
静态或非虚拟方法或属性只是一个内存地址(为了简化事情)。 虚拟方法或属性由表中的条目标识。 此表依赖于定义方法或属性的类。 当您在派生类中重写虚拟成员时,实际上更改了表中的条目以使派生类指向重写方法。 在运行时,访问这样的成员总是在表中。 因此,任何派生类都可以覆盖该条目。
字段没有这种机制,因为它们可以快速访问。
在成员上使用“new”意味着您不想覆盖表中的条目,但是您想要一个新成员(与现有虚拟成员同名,如果您问我,这是一个不好的做法)。
如果通过指向基类的指针访问虚拟成员,则永远不会访问派生类中定义为“new”的成员,这是问题第二部分中提到的差异。
在您的示例中,如果您未在SecondaryTransaction类中重写“Show”,则在SecondaryTransaction实例上调用Show实际上将调用基类(Transaction)中的方法,因此将在基类中使用“Show” class,导致输出:
Primary: name1, state1 Primary: name1, state1
因此,根据您调用的方法(即基类或子类中的一个),代码将具有不同的“前缀”值,这将是可维护性的噩梦 。 我怀疑你可能想要/应该做什么,是在包含“前缀”的Transaction上公开一个属性。
您不能覆盖字段,因为它是基类的实现细节 。 您可以更改受保护字段的值 ,但是通过覆盖它,您基本上就是说我要替换字段而不是值 。
我会做什么(如果我绝对不想/不能使用属性):
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);
}
}
为什么要覆盖受保护的变量,当然要将其设置为重写类中的其他内容(可能在构造函数中)?
你不能因为没有用。 你会通过覆盖一个领域来完成什么?
覆盖一个字段是无稽之谈。 将字段标记为受保护您自动可以在派生类中访问它们。 您可以覆盖函数,属性,因为它在内部使用函数。
你不能因为没有用。 你会通过覆盖一个领域来完成什么? 简单:
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。 我需要一个基类函数,但我还需要一个继承类,我可以操作(增加)内部组类变量中的变量。 这是不可能的!!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.