[英]Can you model an interface where only it's descendants can be implemented?
Assume the following code: 假设以下代码:
namespace Example {
public interface IBase {
string CommonMember { get; set; }
}
public interface IDerived : IBase {
void DoSomething();
}
public interface IComplexDerived : IBase {
IEnumerable<object> Junk { get; }
}
}
I have a similar structure in the project I'm currently working on. 我正在进行的项目中有类似的结构。 The interface
IBase
primarily serves the purpose to be able to keep instances of IDerived
and IComplexDerived
in the same container (like a List<IBase>
) and also not having to repeat common interface member definitions (like CommonMember
in the example). 接口
IBase
主要用于能够将IDerived
和IComplexDerived
实例IComplexDerived
在同一容器中(如List<IBase>
),也不必重复公共接口成员定义(如CommonMember
中的CommonMember
)。
One way this would then be used would be something like this: 然后使用的一种方法是这样的:
public class Foo {
public void Bar( IEnumerable<IBase> instances ) {
foreach( IBase instance in instances ) {
if( instance is IDerived ) { /* do something */ }
else if( instance is IComplexDerived ) { /* do something else */ }
}
}
}
So, nothing would stop the user from implementing IBase
and passing instances of that class into the system. 因此,没有什么能阻止用户实现
IBase
并将该类的实例传递到系统中。 But doing that would be completely useless because the whole library only expects to deal with classes that implement interfaces that were derived from IBase
. 但这样做完全没用,因为整个库只需要处理实现从
IBase
派生的接口的类。
This concept is of course fully documented and shouldn't cause any problems. 这个概念当然是完整记录的,不应该引起任何问题。 However, I was wondering if it would be possible to communicate this through means of the language itself.
但是,我想知道是否可以通过语言本身来传达这一点。 Like having an abstract class, but for interfaces.
就像有一个抽象类,但对于接口。
You might ask why not simply use an abstract class then. 您可能会问为什么不简单地使用抽象类。 The reason for that is that we don't want to impose the requirement to inherit from our class.
原因是我们不想强制要求从我们的类继承。
I'm not sure if this is feasible in your actual case, but I think you could have 我不确定这在你的实际案例中是否可行,但我认为你可以
IComplexDerived
inherit from IDerived
instead of IBase
. IComplexDerived
继承自IDerived
而非IBase
。 IDerived
instead of IBase
, so even a new implementation of IBase
would not type-check (since you require an IEnumerable<IDerived>
) IDerived
而不是IBase
的列表,所以即使是新的IBase
实现也不会进行类型检查(因为你需要一个IEnumerable<IDerived>
) IComplexDerived
would simply implement DoSomething()
in a different way. IComplexDerived
类只会以不同的方式实现DoSomething()
。 By doing this you would let your Bar
method decide polymorphically what DoSomething it needs to call (and avoid checking on the type) Bar
方法以多态方式决定需要调用的DoSomething(并避免检查类型) I mean something like this: 我的意思是这样的:
public interface IBase {
string CommonMember { get; set; }
}
public interface IDerived : IBase {
void DoSomething();
}
//IComplexDerived isnow a IDerived
public interface IComplexDerived : IDerived {
IEnumerable<object> Junk { get; }
}
public class Foo
{
// Bar requires IEnumerable<IDerived> so you can't call it with a collection
// of classes implementing IBase
public void Bar( IEnumerable<IDerived> instances ) {
foreach( IDerived instance in instances ) {
instance.DoSomething(); // DoSomething will "do something else" in
// classes implementing IComplexDerived
}
}
}
One possibility is to remove the common interface from IDerived
and IComplexDervied
and create a wrapper class which takes an instance of one of them and provides the common functionality: 一种可能性是从
IDerived
和IComplexDervied
删除公共接口,并创建一个包装类,它接受其中一个的实例并提供通用功能:
public interface IDerived
{
void DoSomething();
string CommonMember { get; set; }
}
public interface IComplexDerived
{
IEnumerable<object> Junk { get; }
string CommonMember { get; set; }
}
public class EitherDerived : IBase
{
private readonly IDerived derived;
private readonly IComplexDerived complex;
private readonly bool isComplex;
public EitherDerived(IDerived derived)
{
this.derived = derived;
this.isComplex = false;
}
public EitherDerived(IComplexDerived complex)
{
this.complext = complex;
this.isComplex = true;
}
public string CommonMember
{
get
{
return isComplex ? complex.CommonMember : derived.CommonMember;
}
set
{
//...
}
}
public TOut Either<TOut>(Func<IDerived, TOut> mapDerived, Func<IComplexDerived, TOut> mapComplex)
{
return isComplex ? mapComplex(complex) : mapDerived(derived);
}
}
Then you can use this class instead of your IBase
interface if you want to be sure you are dealing with one of those classes: 然后,如果您想确定要处理其中一个类,则可以使用此类而不是
IBase
接口:
private object HandleDerived(IDerived derived) { ... }
private object HandleComplex(IComplexDerived complex) { ... }
public void Bar(IEnumerable<EitherDerived> instances)
{
foreach(var either in instances)
{
object _ = either.SelectEither(HandleDerived, HandleComplex);
}
}
The suggestion to look for a different design altogether was what I ended up doing. 完全寻找不同设计的建议就是我最终做的事情。 Since this is an open source project, we can look at the actual results.
由于这是一个开源项目,我们可以查看实际结果。
IBase
is ITimelineTrackBase
and describes interface members that are common to all derived types. IBase
是ITimelineTrackBase
,描述了所有派生类型共有的接口成员。
IDerived
is ITimelineTrack
and it describes a track on a timeline which consists of a single element with a start and end. IDerived
是ITimelineTrack
,它描述了时间轴上的轨道,该轨道由具有开始和结束的单个元素组成。
IComplexDerived
is IMultiPartTimelineTrack
and it describes a track on a timeline which consists of multiple elements that each have a start and an end. IComplexDerived
是IMultiPartTimelineTrack
,它描述了时间轴上的轨道,该轨道由多个元素组成,每个元素都有一个开头和一个结尾。
Contrary to my earlier plans, I'm not storing these in a List<IBase>
, but I'm using List<IComplexDerived>
. 与我之前的计划相反,我不是将它们存储在
List<IBase>
,而是使用List<IComplexDerived>
。 Or, in terms of the application, a List<IMultiPartTimelineTrack>
. 或者,就应用而言,
List<IMultiPartTimelineTrack>
。
Now I decided to not accept an IBase
anywhere if that's not what I actually want to support in that method. 现在我决定不接受任何地方的
IBase
,如果那不是我真正想要支持的方法。 So the ITimelineTrackBase
is used purely as a base interface and isn't offered as an accepted parameter type anywhere in the library. 因此,
ITimelineTrackBase
纯粹用作基本接口,并且不作为库中任何位置的可接受参数类型提供。
Instead the whole library deals either with single track elements ( ITimelineTrack
) or a collection of those ( IMultiPartTimelineTrack
). 相反,整个库处理单轨道元素(
ITimelineTrack
)或其集合( IMultiPartTimelineTrack
)。 As needed, the former is wrapped into the latter by a helper construct SingleTrackToMultiTrackWrapper
. 根据需要,前者通过辅助构造
SingleTrackToMultiTrackWrapper
包装到后者中。
So instead of making it impossible to implement the interface, I just made it pointless to implement it. 因此,我没有让它无法实现接口,而是让它实现它毫无意义。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.