【问题标题】:Is there any way to subclass a C# interface in a way that *restricts* behavior?有没有办法以*限制*行为的方式对 C# 接口进行子类化?
【发布时间】:2015-01-30 06:09:29
【问题描述】:
public interface IBlackBox<T> {
  bool IsValid();
  T GetValue();
}

public abstract class Box<T>:IBlackBox<T> {
  public bool IsValid() {
    return true;
  }
  public abstract T GetValue();
}

Box 按需生成值;可以是按需计算,也可以是随机的,或者只是一个花哨的可空值等。BlackBox 做同样的事情,只是它可能会过期或变得无效。 Boxes 需要适合 BlackBox 列表(并且强调不是相反的),这决定了继承层次结构。

我不喜欢Box 是一个抽象类而不是一个接口。这引起了两个头痛。首先,这意味着我无法派生struct,这对于仅包含静态值的Box 来说是非常可取的。

第二,这意味着我不能继承任何其他东西。例如,一个预期用途是Dictionary&lt;string,BlackBox&lt;int&gt;&gt;,它本身就是Box,并根据需要对其值求和。没有充分的理由不应该只是子类Dictionary,至少有一个很好的理由应该是:不逐行包装IDictionary 接口!但有些地方我需要Box 的有效性保证,而且似乎我只能拥有其中一个。

可以有一个interface IBox&lt;T&gt; : IBlackBox&lt;T&gt;,它是空的,仅用作“相信我”标签。这意味着在每个实现IBox&lt;T&gt; 的类中复制Box 的单行IsValid(),但这比手工包装IDictionary 的麻烦要少得多。不过,我希望有一个更具体的解决方案。

【问题讨论】:

  • 所以,tl;dr:你不想在每个实现 IBlackBox 的类中重新实现 IsValid 吗?注意:最好将您的问题分成两部分:问题和细节;我刚醒来,很难看到你要来的地方:p
  • 澄清:必须有一个离散的 Box 类型以保证结果。一个盒子必须是一个黑盒子,一个黑盒子不能是一个盒子。我真正想要的是 Box 是一个 IBox 接口,所以我可以选择继承其他类或 makgin 结构。
  • “必须有一个离散的 Box 类型以保证结果” - a) 请在问题本身中进行说明,而不是在 cmets 中,b) 请更准确地解释此要求。 为什么这很重要?请显示一些代码,您可以利用 Box&lt;T&gt; 的优势,保证 IsValid() 方法始终返回 true
  • 我一直在重新阅读这个问题并回到它,感觉就像你所说的那样,继承是错误的方式。也许如果你翻转它,然后有一个单独的界面,它只是作为绝对填充的盒子的标记。如果您需要明确填充的框,则要求标记界面 - 否则,您有一组 IBoxs 并且您确定它们的 either 实际上具有标记界面 或者它们可以转换为IBlackBox,然后你可以使用IsValid方法。这对你有用吗?
  • 我担心你在结构中实现接口的想法会得到一点好处(如果有的话)并且可能会遇到一些其他问题:blogs.msdn.com/b/abhinaba/archive/2005/10/05/477238.aspx

标签: c# inheritance interface abstract-class


【解决方案1】:

这是我想出的最佳解决方案。这只是最基本的,但我已经完成了一个完整的实现(有更好的名字)on GitHub

public interface IBlackBox
public interface IBlackBox<T> : IBlackBox {
  Func<T> GetValue;
}

public interface IBox { }
public interface IBox<T> : IBlackBox<T>, IBox { }

public interface IWrappedBox { }
public interface IWrappedBox<T> : IBox<IBlackBox<T>>, IWrappedBox { }

public interface IEmptyBox { }
public static class EmptyBox {
  public static EmptyBox<T> Get<T>() {
    return EmptyBox<T>.value;
  }
  sealed class EmptyBox<T> : IBlackBox<T>, IEmptyBox {
    public static readonly EmptyBox<T> value = new EmptyBox<T>();
    public Func<T> GetValue {
      get {
        throw new NotSupportedException
          ("Cannot extract a value from an empty box.");
      }
    }
  }
}

非泛型接口允许在类型级别进行测试,例如:

IBlackBox something = someWrappedBox.GetValue();
if (something is IEmptyBox)
  HandleEmpty();
else
  HandleSomething(something.GetValue());

这还是要结合编译器无法检查的三个规则,但是结构很容易符合:

  • IBlackBox&lt;T&gt;IBlackBoxIEmptyBox 不能直接继承。
  • 只有IWrappedBox&lt;T&gt;.GetValue() 可以返回基数IBlackBox&lt;T&gt;
  • 返回值不能具体为任何IEmptyBox

IEmptyBox 保证只有在明确允许时才会出现; IBox&lt;T&gt; 保证包含一个好的T;而所有能被继承的都是一个接口。任务完成!

【讨论】:

    【解决方案2】:

    我建议有一个IValidBox&lt;T&gt; : IBlackBox&lt;T&gt;,该合同规定任何合法实现可能永远不会让IsValid返回false,并且消费者有权调用GetValue而不检查IsValid。此外,为了减少样板,您可以定义一个实现IValidBox&lt;T&gt; 的抽象类ValidBoxBase&lt;T&gt;,以便不需要继承任何其他东西的IValidBox&lt;T&gt; 的实现可以节省一些样板。在这个简短的例子中不多,但在更充实的场景中可能会有很多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-24
      • 2011-01-07
      • 1970-01-01
      • 1970-01-01
      • 2017-06-22
      • 2014-12-30
      • 2010-09-09
      相关资源
      最近更新 更多