我如何在C#中创建一个可继承的不可变属性?

时间:2012-01-16 03:42:48

标签: c# inheritance interface properties

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

interface IFoo
{
    Int32 Id { get; }
}

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

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

我的想法是它会继承Id,然后在我的子界面中我会使它可以设置。令我惊讶的是,这不起作用。相反,我得到一个警告让我知道我实际上用IMutableFoo中的Id覆盖了IFoo中的Id。我试过改变{set; } 要得到;组;具有相同的结果。我该怎么做?在Java中,我只需添加一个setId方法。我如何在C#中执行此操作?谢谢!

4 个答案:

答案 0 :(得分:4)

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

MSDN documentation包含短语“接口可以继承其他接口”,但这是恕我直言,因为这里没有继承。 “派生”界面简单地描述了必须实现的一组更大的成员而不是“基础”界面。

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; }
}

不幸的是,没有强制实施者执行此操作的机制,但是一些好的文档可以在这里发挥很大作用 - 如果IFoo和{{1}之间的关系更是如此很直观。

答案 1 :(得分:2)

在后代界面中重新声明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断开连接。

答案 2 :(得分:1)

你可以做到

  public interface IFoo
    {
        Int32 Id { get; }
    }

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

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

答案 3 :(得分:0)

将一个属性称为“不可变”与将其称为“只读”之间存在巨大差异。 “不可变”的对象属性是一种永远不会因任何原因而改变的对象属性,只要该对象存在即可。相比之下,“只读”属性是使用属性设置器无法更改的属性,但可能通过其他方式进行更改。

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