2017-08-08 34 views
1

我想通過類型實現編譯時依賴驗證。最終這源於使用依賴注入,我厭倦了忘記添加一個依賴到ServiceCollection。比方說,我有一個建造者,我不想添加一個項目,除非它所依賴的東西已經被添加。我認爲這可以通過一個例子來最好地說明。我想下面的失敗:構建模式,增加返回類型?

var myBuilder = new MyBuilder(configuration) 
    .AddMyServiceRequiresThing()  // dependency not satisfied, type error 
    .AddMySecondServiceRequiresThing2() // dependency not satisfied, type error 
    .AddMyThing() 
    .AddMyThing2(); 

但我想下面的成功:

var myBuilder = MyBuilder(configuration) 
    .AddThing() 
    .AddMyServiceRequiresThing() // dependency satisfied by AddThing() 
    .AddThing2() 
    .AddMyService2RequiresThing2(); // dependency satisfied by AddThing2() 

當然下面還有會成功:當

var myBuilder = MyBuilder(configuration) 
    .AddThing() 
    .AddThing2() 
    .AddMyServiceRequiresThing() // dependency satisfied by AddThing() 
    .AddMyService2RequiresThing2(); // dependency satisfied by AddThing2() 

從本質上講,增加一件「事情」,我希望建築師的類型得到增強,因爲它現在提供了這個東西。但是由於「MyService」和「MyService2」可能需要不同的東西,它們不能只返回原始的構建器類型(不增強),並且它們不能僅僅返回它們需要的東西,因爲它們的需求可能不相關。

我嘗試了一些仿製藥,但已經無處可尋。我可以通過運行時邏輯強制執行此操作,但我希望類型系統爲我執行此操作。 c#的類型系統足夠強大,可以執行此操作嗎?所有的信息在編譯時可用,我只是不知道是否可以表達。

+0

「c#的類型系統是否足夠強大?」還有沒有其他靜態類型的語言這種事情就像你期望的那樣工作? –

+1

您可以通過以下方式測試編譯時間: 將Bool變量初始化爲false。 如果您在Addthing()中將該bool變量設置爲true,並在requiresthing()中檢查它,如果visual studio警告您該部分永遠不會到達,您可以推斷它在編譯時檢查。 此外,異常可以照顧編程錯誤。 – gismo

+0

@WiktorZychla TBH對於像Haskell這樣的更強大的類型系統或者(在這裏插入你喜歡的類型系統),我不太瞭解這個類型的模式是否可行。 – jrk

回答

1

不確定查找這樣的功能是否正確。在您提供的示例中,它可能是合乎邏輯的,但MyBuilder對象可以在代碼中不會被跟蹤的任何部分中構建和/或更改,甚至可以在編譯時無法跟蹤的不同線程中時間。 即使在您的示例中,也不清楚MyBuilder的構造函數中發生了什麼。 您也可以在將更新其數據生成器類的方法,然後你會調用構建功能

class Data1 
{ 
    public void Update(MyBuilder builder) 
    { 
     builder.AddThing(); // or anything else 
    } 
} 

而且沒有人可以知道究竟可以在功能

的對象發生

如果「物聯網」的量或sequense沒有問題也許會更好,有不同的製造商,包括像

class Builder2 
{ 
    public Builder2(MyBuilder builder) 
    { 
     _thing1 = builder.Thing1; 
     AddThing2(); 
    } 
    public XXX AddMyService2RequiresThing2() 
    { 
     ... 
    } 
} 

複合建設者看看的表達式樹是如何在C#中實現它的soun ds接近你想要的。它允許您構建不同的表達式/委託,並且對編譯階段有很好的控制。不要一概而論的事情你不想一概而論

可以擴展碼到不同的對象: - 通過構建 生產 - 存儲在生成器並提供額外的數據,將其 - 有單「的事情」 /座responcibility

public class Builder 
{ 
    // Builder will have access to all fields of implementation 
    // which will be hidden from other part of code 
    private ImplThinger1 ImplThinger1; 
    // other Thinger implementations 

    public IThinger1 AddThing1() 
    { 
     return ImplThinger1 ?? (ImplThinger1 = new ImplThinger1()); 
    } 
} 

public interface IThinger1 
{ 
    void AddServiceReqThing1(); 
} 

public class ImplThinger1 : IThinger1 
{ 
    // accessible in builder and not outside 
    // if it's not created outside 
    public Thing Data { get; } 

    public void AddServiceReqThing1() 
    { 
     // Stuff 
    } 
} 

在結束建設者將所有已添加的實現,這將是不可能的,即使實施,將直接增加服務只能通過IThinger接口實現對生成器,將使用生成器來構建,被創建IThinger將被創建它不會在投入Builder

+0

將東西添加到最初構建器鏈之外的構建器將由構建器決定是否添加了該東西。那沒問題。 – jrk

+0

顯然,SO不允許對評論進行冗長的編輯。並返回保存評論。 TIL。 我想如果提供者修改了構建器的返回類型,以這種方式說構建器現在提供了這個東西,不管它是什麼。考慮一個「AddMyCacheService」方法,該方法會修改生成器以宣傳它現在支持通過DI使用IMyCacheService。在調用AddMyCacheService方法之前,任何IMyCacheService使用者都無法將其自身添加到構建器中。 – jrk

+0

聽起來更好的方法是在每次構建之後運行一個單元測試,並在錯誤的設置上失敗。 – ASpirin