簡體   English   中英

你能模擬一個只能實現它的后代的界面嗎?

[英]Can you model an interface where only it's descendants can be implemented?

假設以下代碼:

namespace Example {
  public interface IBase {
    string CommonMember { get; set; }
  }

  public interface IDerived : IBase {
    void DoSomething();
  }

  public interface IComplexDerived : IBase {
    IEnumerable<object> Junk { get; }
  }
}

我正在進行的項目中有類似的結構。 接口IBase主要用於能夠將IDerivedIComplexDerived實例IComplexDerived在同一容器中(如List<IBase> ),也不必重復公共接口成員定義(如CommonMember中的CommonMember )。

然后使用的一種方法是這樣的:

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 */ }
    }
  }
}

因此,沒有什么能阻止用戶實現IBase並將該類的實例傳遞到系統中。 但這樣做完全沒用,因為整個庫只需要處理實現從IBase 派生的接口的類。

這個概念當然是完整記錄的,不應該引起任何問題。 但是,我想知道是否可以通過語言本身來傳達這一點。 就像有一個抽象類,但對於接口。

您可能會問為什么不簡單地使用抽象類。 原因是我們不想強制要求從我們的類繼承。

我不確定這在你的實際案例中是否可行,但我認為你可以

  • IComplexDerived繼承自IDerived而非IBase
  • 然后你會有一個IDerived而不是IBase的列表,所以即使是新的IBase實現也不會進行類型檢查(因為你需要一個IEnumerable<IDerived>
  • 繼承自IComplexDerived類只會以不同的方式實現DoSomething() 通過執行此操作,您可以讓Bar方法以多態方式決定需要調用的DoSomething(並避免檢查類型)

我的意思是這樣的:

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

一種可能性是從IDerivedIComplexDervied刪除公共接口,並創建一個包裝類,它接受其中一個的實例並提供通用功能:

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接口:

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

完全尋找不同設計的建議就是我最終做的事情。 由於這是一個開源項目,我們可以查看實際結果。

  • IBaseITimelineTrackBase ,描述了所有派生類型共有的接口成員。

  • IDerivedITimelineTrack ,它描述了時間軸上的軌道,該軌道由具有開始和結束的單個元素組成。

  • IComplexDerivedIMultiPartTimelineTrack ,它描述了時間軸上的軌道,該軌道由多個元素組成,每個元素都有一個開頭和一個結尾。

與我之前的計划相反,我不是將它們存儲在List<IBase> ,而是使用List<IComplexDerived> 或者,就應用而言, List<IMultiPartTimelineTrack>

現在我決定不接受任何地方的IBase ,如果那不是我真正想要支持的方法。 因此, ITimelineTrackBase純粹用作基本接口,並且不作為庫中任何位置的可接受參數類型提供。

相反,整個庫處理單軌道元素( ITimelineTrack )或其集合( IMultiPartTimelineTrack )。 根據需要,前者通過輔助構造SingleTrackToMultiTrackWrapper包裝到后者中。

因此,我沒有讓它無法實現接口,而是讓它實現它毫無意義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM