【问题标题】:Why can I not add a set accessor to an overriden property?为什么我不能将 set 访问器添加到覆盖属性?
【发布时间】:2009-08-04 12:26:16
【问题描述】:

在一个基类中我有这个属性:

public virtual string Text 
{
    get { return text; }
}

我想覆盖它并返回不同的文本,但我也希望能够设置文本,所以我这样做了:

public override string Text
{
    get { return differentText; }
    set { differentText = value; }
}

但这不起作用。我在set 下得到一个红色波浪线,说我无法覆盖,因为它没有设置访问器。为什么这是个问题?我该怎么办?

【问题讨论】:

  • 我也想做同样的事情。应该可以的。
  • 有可能,但需要“中产阶级”。此外,在许多情况下这可能不是一个好主意,但也有一些例外,例如在覆盖抽象属性时。基地:public abstract string Text { get; }。中间(继承基):protected internal string text;public sealed override string Text { get { return text; } }。孩子(继承中间):public new string Text { get { return text; } set { text = value; } }。受保护的内部修饰符和密封的覆盖确保封装不会在您的程序集之外被破坏。
  • @AnorZaken:这不是压倒一切,而是遮蔽。它也不需要“中产”阶级。
  • @Guffa 它既是覆盖又是阴影,由于 c# 不允许两者在同一个类中,所以需要中间类
  • @AnorZaken:不能两者兼有,阴影不是压倒一切的。在中间类中覆盖它不会使其在子类中被覆盖,它仍然是阴影。

标签: c# inheritance properties accessor


【解决方案1】:
public virtual string Text 
{
    get { return text; }
    protected set {}
}

像这样更改基类属性,您正在尝试覆盖不存在的 set 方法

【讨论】:

  • 您不能将受保护的设置器覆盖为子类中的公共设置器。
【解决方案2】:

在您的第二个代码块中,您正在创建一个公共 set 方法,但声明中的“覆盖”一词使编译器在基类中寻找具有相同签名的方法。因为它找不到那个方法,所以它不允许你创建你的集合。

正如 ArsenMkrt 所说,您可以更改基本声明以包含受保护的集。这将允许您覆盖它,但由于您仍然无法更改签名,因此您无法在子类中将此方法提升为 public,因此您发布的代码仍然不起作用。

相反,您需要向您的基类添加一个不执行任何操作的公共虚拟 set 方法(或者如果您尝试调用它甚至会引发异常),但这与该类的用户所期望的相反如果您这样做(我不会推荐),请确保它有充分的文档记录以使用户不会错过它:

///<summary>
///Get the Text value of the object
///NOTE: Setting the value is not supported by this class but may be supported by child classes
///</summary>
public virtual string Text 
{
    get { return text; }
    set { }
}

//using the class

BaseClass.Text = "Wibble";
if (BaseClass.Text == "Wibble")
{
    //Won't get here (unless the default value is "Wibble")
}

否则将集合声明为子类中的单独方法:

public override string Text
{
    get { return differentText; }
}

public void SetText(string value)
{
    differentText = value;
}

【讨论】:

  • 我不喜欢它,但我想我将不得不做一些事情,比如有一个单独的 SetText 方法或其他东西......谢谢:)
  • 嗯,这也可能是架构问题。为什么用户不能在基类中设置文本值? 真的两个类中的属性相同吗?需要这样的模式似乎是一种通常的要求。
  • 嗯,它是许多不同的参数类,它们包含不同类型的值。而且我希望他们都能够将自己表示为用户可以阅读的文本值(例如用于打印)。但我不希望所有参数都由文本设置(解析比打印更烦人)。比如一个日期参数我想打印日期,但是我不需要通过文本来设置日期,因为设置是通过日期选择器来完成的。
【解决方案3】:

您希望在使用子类型时公开更多功能。听起来你不想覆盖,你想阴影。只需使用 new 关键字将只读 Text 属性隐藏在您的可读/可写属性下即可。

在基类中:

protected string text;
public string Text 
{
    get { return text; }
}

在派生类中:

new public string Text 
{
    get { return text; }
    set { text = value; }
}

【讨论】:

  • 你可以同时覆盖和覆盖吗?
  • 不,它们的意思不同。是否阴影或覆盖决定了即使在转换为基本类型之后 Text 的行为是否也会受到影响。查看这个问题以获得更好的解释:stackoverflow.com/questions/673779/what-is-shadowing
【解决方案4】:

这是一个问题,因为您正在破坏封装。您不能覆盖某些内容并使其更易于访问,这会将有关封装的所有内容都抛在脑后。

这是规则,它也适用于你的情况,即使你实际上暴露了一些不是原始值的东西。

没有办法完全按照您的尝试进行。您必须在基类中创建一个 setter,或者使用不同的方法来设置新值。

【讨论】:

  • 子类不能破坏封装,因为除非封装已经在基类中破坏(如果子类不应该能够访问属性的支持字段)。此外,基类已将属性声明为虚拟,这意味着它应该可以更改其行为 - 当属性/方法为虚拟时,任何事情都会发生! (当然,只要你遵守它的文档。)它是否应该是虚拟的,或者这是否破坏了基类的封装,这是真正的问题,这取决于。跨度>
  • @AnorZaken:你在说什么?仅仅因为成员是虚拟的,并不意味着您可以随意覆盖它。
【解决方案5】:

您可以从基类中隐藏该属性:

public new string Text
{
    get { return differentText; }
    set { differentText = value; }
}

但在这种情况下,只有在通过这种类型的变量而不是基类型来操作对象时才会使用该属性

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多