【问题标题】:Abstract getter with concrete setter in C#C# 中带有具体设置器的抽象 getter
【发布时间】:2011-05-25 18:45:16
【问题描述】:

我正在尝试为实现IList 的只读集合编写一个抽象基类。这样的基类应该实现 set-indexer 以抛出 NotSupportedException,但将 get-indexer 保留为抽象。 C# 允许这种情况吗?到目前为止,这是我所拥有的:

public abstract class ReadOnlyList : IList {

    public bool IsReadOnly { get { return true; } }

    public object this[int index] {
        get {
            // there is nothing to put here! Ideally it would be left abstract.
            throw new NotImplementedException();
        }
        set {
            throw new NotSupportedException("The collection is read-only.");
        }
    }

    // other members of IList ...
}

理想情况下,ReadOnlyList 将能够实现 setter,但保留 getter 抽象。是否有任何语法允许这样做?

【问题讨论】:

  • 如果这不仅仅是一个练习,你在框架中有 ReadOnlyCollection
  • 谢谢;我通常使用 ReadOnlyCollection,尽管这种特殊情况需要 IList 的自定义实现。我确实倾向于将我的场景简化为提出问题所需的最低限度。

标签: c#


【解决方案1】:

将工作委托给受保护的成员,然后您可以根据所需的行为将其标记为抽象或虚拟。试试这样的:

 // implementor MUST override
 protected abstract object IndexerGet(int index);

 // implementor can override if he wants..
 protected virtual void IndexerSet(int index, object value) 
 {
   throw new NotSupportedException("The collection is read-only.");
 }

 public object this[int index] {
   get {
     return IndexerGet(index);
   }
   set {
     IndexerSet(index, value);
   }
 }

【讨论】:

  • IndexerSet 应该可能不是虚拟的 - 实际上,只需将 NotSupportedException 扔到 setter 本身就可以了。
  • 感谢 m0sa。我接受了这个作为答案,因为它是实现实现 setter 和强制派生类实现 getter 目标的唯一答案。更不用说它几乎是迄今为止唯一可以编译的答案!
【解决方案2】:

如果您希望它是只读集合,那么为什么要实现IList

您应该改为实现IEnumerable

【讨论】:

  • IList 明确permitted 是只读的。 IEnumerable 不提供随机访问。
  • 虽然这不像ReadOnlyCollection<> 解决方案那样功能齐全,但在实践中它通常就足够了,并且具有作为一个更简单的API 的优点——这才是最重要的,对吧?我只会在 IEnumerable 不足的情况下提倡 ReadOnlyCollection。
  • 另外,IEnumerable 玩家更喜欢类型系统:我通常使用方法来打乱和排序集合 - 这些在 IList 上工作。只读 ILists 将在运行时失败,而传递 IEnumerable 将在编译时失败;这是更好的行为。
【解决方案3】:

你不需要二传手

public abstract object this[int index] { get; }

【讨论】:

  • 阅读其他相同的答案。
【解决方案4】:

你为什么想要有 setter - 没有 setter 将表明它的只读属性。

编辑:jgauffin 指出 setter 来自 IList。所以你可能有两种选择:

  1. 私下实现 IList 并为索引器提供公共抽象 getter(虽然不能 100% 确定我是否可以拥有两个索引器)。
  2. Indexer getter 将调用 GetItem 等抽象方法。

【讨论】:

  • 因为IList 定义了索引器。
  • 界面上是required
【解决方案5】:

没有。如果您能找到一种方法,它确实违反了 Liskov 替换原则。

在此处阅读有关 LSP 的更多信息:Can you explain Liskov Substitution Principle with a good C# example?

【讨论】:

  • 这如何破坏 LSP?具体的子类必须提供具体的 getter 实现。如果这样做,该子类应该(不存在其他问题)是行为良好的IListallowed 是只读的)。
  • 因为任何使用IList 的人都希望能够获取和设置列表中的项目。 .Net 框架中的ReadOnlyCollection 也违反了 LSP,因为它不满足该要求。
  • 创建一个只定义访问列表的方法的接口并使用IWritableList 之类的方法派生它是实现 LSP 的一种方式。 IsReadOnly 一种解决方法。
  • @jg,你一直这么说,但它仍然不正确。 IList.Item 文档非常清楚,如果 setter 被调用并且它恰好是只读的,那么行为良好的 IList 必须(不仅仅是可能)抛出一个 NotSupportedException。 ReadOnly 没有被“添加”。它是原始界面的一部分。如果客户端代码正确编写以处理原始IList,则它不会出现只读列表的问题,因此不存在LSP问题。
  • 我收回关于 IndexOutOfRangeException 与 ArgumentOutOfRangeException 的声明 - 我本可以发誓存在这样的不一致,但在 LINQpad 中尝试这一点表明,转换为 IList 的数组会适当地引发 ArgumentOutOfRangeException
【解决方案6】:

不,您不能拥有一个具有成员摘要的属性,而另一个已实现。我要做的是将整个属性设置为抽象并在继承者处重新定义它以做任何你想做的事情。

【讨论】:

  • 感谢 SoMoS;尽管我接受了 m0sa 的回答作为回答问题的答案,但我最终还是采用了您建议的方法,以避免额外的间接性。
【解决方案7】:

为什么不将 setter 设为私有?

public abstract class ReadOnlyList : IList
    {

        public bool IsReadOnly { get { return true; } }

        public object this[int index]
        {
            get
            {
                // there is nothing to put here! Ideally it would be left abstract.
                throw new NotImplementedException();
            }
            private set
            {
                // your private implementation
            }
        }

        // other members of IList ...
    }

【讨论】:

  • 接口要求它是公开的,而您没有解决保持 getter 抽象的愿望。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 2016-09-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多