C# 你能为一个只有它';s的后代可以实现吗?
假设以下代码:C# 你能为一个只有它';s的后代可以实现吗?,c#,C#,假设以下代码: namespace Example { public interface IBase { string CommonMember { get; set; } } public interface IDerived : IBase { void DoSomething(); } public interface IComplexDerived : IBase { IEnumerable<object> Junk { get
namespace Example {
public interface IBase {
string CommonMember { get; set; }
}
public interface IDerived : IBase {
void DoSomething();
}
public interface IComplexDerived : IBase {
IEnumerable<object> Junk { get; }
}
}
因此,没有任何东西可以阻止用户实现IBase
,并将该类的实例传递到系统中。但是这样做是完全无用的,因为整个库只希望处理实现从IBase
派生的接口的类
当然,这一概念有充分的文档记录,不应引起任何问题。然而,我想知道是否有可能通过语言本身来传达这一点。类似于有一个抽象类,但用于接口
您可能会问,为什么不简单地使用抽象类呢。原因是我们不想强加从类继承的要求。我不确定这在您的实际情况下是否可行,但我认为您可以这样做
继承自IComplexDerived
而不是IDerived
IBase
- 然后,您将有一个
列表,而不是IDerived
,因此即使是IBase
的新实现也不会进行类型检查(因为您需要IBase
)IEnumerable
- 继承自
的类将以不同的方式实现IComplexDerived
。通过这样做,您可以让您的DoSomething()
方法以多态方式决定它需要调用什么DoSomething(并避免检查类型)Bar
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 */ }
}
}
}
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
}
}
}
公共接口IBase{
字符串CommonMember{get;set;}
}
公共接口驱动:IBase{
无效剂量();
}
//IComplexDerived现在是一个理想的选择
公共接口IComplexDerived:IDerived{
IEnumerable垃圾{get;}
}
公开课Foo
{
//Bar需要IEnumerable,所以不能用集合调用它
//实现IBase的类的数量
公共空栏(IEnumerable实例){
foreach(实例中的IDERIVE实例){
instance.DoSomething();//DoSomething将在
//实现IComplexDerived的类
}
}
}
一种可能性是从IDerived
和icomplexserviced
中删除公共接口,并创建一个包装类,该包装类采用其中一个实例并提供公共功能:
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);
}
}
我最终的建议是寻找一种完全不同的设计。由于这是一个开源项目,我们可以查看实际结果
是并描述了所有派生类型通用的接口成员IBase
是,它描述了时间轴上的一条轨迹,该轨迹由一个具有开始和结束的元素组成IDerived
是,它描述了由多个元素组成的时间轴上的轨迹,每个元素都有一个开始和一个结束IComplexDerived
列表中,而是使用列表。或者,就应用而言,是一个列表
现在我决定在任何地方都不接受IBase
,如果这不是我在该方法中真正想要支持的。因此,ITimelineTrackBase
纯粹用作基本接口,而不是作为库中任何位置的可接受参数类型提供
相反,整个库要么处理单轨元素(ITimelineTrack
),要么处理这些元素的集合(IMultiPartTimelineTrack
)。根据需要,前者由助手构造包装到后者中
因此,我没有让接口无法实现,而是让它实现起来毫无意义。我认为设计这样的东西不是一个好主意。您有一个公共接口,但仍然需要检查类型以决定要执行的代码。如果有人创建了从IBase
派生的第三个接口并实现了它,您会怎么做?或者如果你想引入第三个接口?您必须调整IBase
的所有使用者。如果您仍在检查特定类型,接口的用途是什么?接口本身与抽象类有很多共同之处,因为您无法实例化它。我想你只需要告诉你的用户(程序员)不要直接实现IBase。您可能想做一些运行时检查,尽管我不会在这方面花费太多代码。@DanielHilgarth:这一点很好。我会看看是否能想出一个更好的方法。我将此标记为解决方案,尽管我采用了与评论中建议的完全不同的方法。@oliversalzburg如果你能想出一个更好的解决方案,你可以为你的问题添加一个答案并接受它。当然,我只是觉得我的解决方案对我的情况非常具体,对未来的访问者没有什么帮助。但是,你是对的,为了完整起见,我将添加我最后得到的答案。
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);
}
}