Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/267.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 你能为一个只有它';s的后代可以实现吗?_C# - Fatal编程技术网

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()
    。通过这样做,您可以让您的
    Bar
    方法以多态方式决定它需要调用什么DoSomething(并避免检查类型)
我的意思是这样的:

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);
    }
}