有没有办法以*限制*行为的方式对 C# 接口进行子类化?
Is there any way to subclass a C# interface in a way that *restricts* behavior?
public interface IBlackBox<T> {
bool IsValid();
T GetValue();
}
public abstract class Box<T>:IBlackBox<T> {
public bool IsValid() {
return true;
}
public abstract T GetValue();
}
A Box
按需产生一个值;可以是按需计算,或者随机,或者只是一个奇特的可为空等。 BlackBox
做同样的事情,除了它可能会过期或以其他方式变得无效。 Box
es 需要适合 BlackBox
列表(并且强调不是相反),这决定了继承层次结构。
我不喜欢的是 Box
是抽象的 class 而不是接口。这引起了两个令人头疼的问题。首先,这意味着我无法导出 struct
,在 Box
仅包含静态值的情况下,这将是非常可取的。
其次,这意味着我无法再class 任何其他内容。例如,一个预期用途是 Dictionary<string,BlackBox<int>>
本身就是 Box
并按需求和其值。没有充分的理由不应该只是 subclass Dictionary
,至少有一个很好的理由应该是:不要逐行包装 IDictionary
接口!但是有些地方需要Box
有效性保证,好像只能有一个。
我 可以 有一个 interface IBox<T> : IBlackBox<T>
是空的,仅用作 "trust me" 标签。这意味着在实现 IBox<T>
的每个 class 中复制 Box
的单行 IsValid()
,但这比手动包装 IDictionary
麻烦得多。不过,我希望有一个更具体的解决方案。
这是我想出的最佳解决方案。这只是骨架,但我提供了完整的实现(使用更好的名称)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>
、IBlackBox
和IEmptyBox
不能直接继承。
- 只有
IWrappedBox<T>.GetValue()
可以return基地IBlackBox<T>
.
- Return 值不能具体为任何
IEmptyBox
.
IEmptyBox
保证只在明确允许的情况下出现; IBox<T>
保证包含好的T
;凡是可以继承的都是接口。任务完成!
我建议制定一个 IValidBox<T> : IBlackBox<T>
合同,其中规定没有 合法 实施可能 IsValid
return 错误,并且消费者有权调用 GetValue
而无需检查 IsValid
。此外,为了减少样板文件,您可以定义一个实现 IValidBox<T>
的抽象 class ValidBoxBase<T>
,这样不需要继承任何其他内容的 IValidBox<T>
的实现可以节省一些样板。在这个简短的例子中并不多,但在更充实的场景中可能会很重要。
public interface IBlackBox<T> {
bool IsValid();
T GetValue();
}
public abstract class Box<T>:IBlackBox<T> {
public bool IsValid() {
return true;
}
public abstract T GetValue();
}
A Box
按需产生一个值;可以是按需计算,或者随机,或者只是一个奇特的可为空等。 BlackBox
做同样的事情,除了它可能会过期或以其他方式变得无效。 Box
es 需要适合 BlackBox
列表(并且强调不是相反),这决定了继承层次结构。
我不喜欢的是 Box
是抽象的 class 而不是接口。这引起了两个令人头疼的问题。首先,这意味着我无法导出 struct
,在 Box
仅包含静态值的情况下,这将是非常可取的。
其次,这意味着我无法再class 任何其他内容。例如,一个预期用途是 Dictionary<string,BlackBox<int>>
本身就是 Box
并按需求和其值。没有充分的理由不应该只是 subclass Dictionary
,至少有一个很好的理由应该是:不要逐行包装 IDictionary
接口!但是有些地方需要Box
有效性保证,好像只能有一个。
我 可以 有一个 interface IBox<T> : IBlackBox<T>
是空的,仅用作 "trust me" 标签。这意味着在实现 IBox<T>
的每个 class 中复制 Box
的单行 IsValid()
,但这比手动包装 IDictionary
麻烦得多。不过,我希望有一个更具体的解决方案。
这是我想出的最佳解决方案。这只是骨架,但我提供了完整的实现(使用更好的名称)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>
、IBlackBox
和IEmptyBox
不能直接继承。- 只有
IWrappedBox<T>.GetValue()
可以return基地IBlackBox<T>
. - Return 值不能具体为任何
IEmptyBox
.
IEmptyBox
保证只在明确允许的情况下出现; IBox<T>
保证包含好的T
;凡是可以继承的都是接口。任务完成!
我建议制定一个 IValidBox<T> : IBlackBox<T>
合同,其中规定没有 合法 实施可能 IsValid
return 错误,并且消费者有权调用 GetValue
而无需检查 IsValid
。此外,为了减少样板文件,您可以定义一个实现 IValidBox<T>
的抽象 class ValidBoxBase<T>
,这样不需要继承任何其他内容的 IValidBox<T>
的实现可以节省一些样板。在这个简短的例子中并不多,但在更充实的场景中可能会很重要。