如何在C#中使继承的不可变属性可变

本文关键字:不可变 属性 继承 | 更新日期: 2023-09-27 18:20:27

考虑我有一个包含以下属性的接口

interface IFoo
{
    Int32 Id { get; }
}

现在说我想创建一个IMutableFoo接口。从逻辑上讲,我认为以下是正确的:

interface IMutableFoo: IFoo
{
    Int32 Id { set; }
}

我的想法是,它会继承Id,然后在我的子界面中使其可设置。令我惊讶的是,这并没有奏效。相反,我收到了一个警告,让我知道我实际上正在用IMutableFoo中的Id覆盖IFoo中的Id。我尝试将{set;}更改为{get;set;},结果相同。我该怎么做?在Java中,我只需添加一个setId方法。我如何在C#中做到这一点?谢谢

如何在C#中使继承的不可变属性可变

除了用一个新的读/写属性定义一个新接口之外,你什么都做不了。这是因为在谈论接口时没有继承的概念;相反,像IMutableFoo这样的接口承诺其实现者也将实现接口IFoo。然而,这两个接口都是独立定义的,并且保持独立。

MSDN文档包含短语"接口可以继承其他接口",但这是IMHO的误导,因为这里没有继承。"派生"接口简单地描述了一组比"基本"接口更大的必须实现的成员。

接口(如IMutableFoo)的实现者可以通过显式实现IFoo并在IMutableFoo.IdIFoo.Id的getter之间共享代码来毫无问题地提供目标语义:

class Foo : IMutableFoo {
    // IFoo is implemented explicitly
    // "this" is of type Foo, and since IMutableFoo is implemented
    // implicitly below, this.Id accesses the Id declared in IMutableFoo
    Int32 IFoo.Id { get { return this.Id; } }
    // IMutableFoo is implemented implicitly
    // It could also be implemented explicitly, but the body of the
    // IFoo.Id getter would need to change ("this" would no longer work)
    public Int32 Id { get; set; }
}

不幸的是,没有强制实现者这样做的机制,但一些好的文档可以在这里发挥很大作用——如果IFooIMutableFoo之间的关系是直观的,那就更是如此。

在派生接口中重新声明Id确实隐藏了父接口中的Id。

为此,我使用了两种不同的解决方案,其中包括我不特别喜欢的权衡。

1-使用抽象类,甚至非抽象类,而不是可变接口。

interface IFoo {
    Int32 Id { get; }
}
abstract class MutableFoo: IFoo {
    public abstract Int32 Id {get; set;}
}

最大的缺点是库的用户无法再对接口进行编程。

2-使用方法而不是属性。

interface IFoo {
    Int32 Id { get; }
}
interface IMutableFoo: IFoo {
    void SetId(Int32 value);
}

这并不理想,因为setter不是惯用的,而且看起来与getter断开了连接。

您可以进行

  public interface IFoo
    {
        Int32 Id { get; }
    }
   public interface IFooMu : IFoo
    {
        new Int32 Id { get; set; }
    }  
    class Foo : IFooMu
    {
        public Int32 Id { get; set; }
    }

说一个属性是"不可变的"和说它是"只读的"之间有很大的区别。只要有问题的对象存在,"不可变"的对象属性就永远不会因为任何原因而改变。相比之下,"只读"属性是不能使用属性设置器进行更改的属性,但可能会通过其他方式进行更改。

您的问题似乎是关于如何使对象以合理的方式同时支持只读和读写接口;基思·尼古拉斯的回答说明了正确的方法。请注意,如果使用显式接口实现,或者如果在vb.net而不是c#中编写类,则有必要同时实现IFooMu.Id和IFoo.Id;这是.net强加的一个烦人的要求,但c允许一个公共属性作为只读、读写和只读属性的隐式定义。