有没有办法以*限制*行为的方式子类化C#接口?

时间:2015-01-30 06:09:30

标签: c# inheritance interface abstract-class

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做同样的事情,除了它可能会过期或以其他方式变得无效。 Box需要适合BlackBox列表(并且强调不是反向),这决定了继承层次结构。

我不喜欢的是Box是抽象类而不是接口。这导致两个头痛。首先,这意味着我无法派生struct,在Box仅包含静态值的情况下,这将是非常优选的。

其次,这意味着我不能将其他任何子类化。例如,一个预期用途是Dictionary<string,BlackBox<int>>,它本身就是Box,并根据需要对其值进行求和。没有充分的理由这不应该仅仅是Dictionary的子类,并且至少有一个非常好的理由:它不能逐行包装IDictionary接口!但有些地方我需要Box保证有效性,似乎我只能拥有一个或另一个。

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

2 个答案:

答案 0 :(得分:2)

这是我提出的最佳解决方案。这只是一块骨头,但我已经完成了一个完整的实现(更好的名字)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<T>IBlackBoxIEmptyBox无法直接继承。
  • 只有IWrappedBox<T>.GetValue()才能返回基础IBlackBox<T>
  • 返回值不能特别为IEmptyBox

IEmptyBox只有在明确允许时才会出现; IBox<T>保证包含好T;并且可以继承的所有东西都是一个接口。任务完成了!

答案 1 :(得分:0)

我建议使用一个IValidBox<T> : IBlackBox<T>合同,该合同规定没有合法实施可能会IsValid返回false,并且消费者有权致电GetValue未检查IsValid 。另外,为了减少样板,你可以定义一个实现ValidBoxBase<T>的抽象类IValidBox<T>,这样不需要继承任何东西的IValidBox<T>实现就可以节省一些样板。在这个简短的例子中并不多,但在更加充实的场景中可能会有很多。