簡體   English   中英

C#有抽象類和接口,它是否也有“mixins”?

[英]C# has abstract classes and interfaces, should it also have “mixins”?

我經常遇到一個案例,我希望所有類的集合都擁有類似的邏輯。 例如,也許我希望BirdAirplane能夠Fly() 如果您正在考慮“戰略模式”,我會同意,但即使采用策略,有時也無法避免重復代碼。

例如,讓我們說以下適用(這與我最近遇到的真實情況非常相似):

  1. BirdAirplane需要保存一個實現IFlyBehavior的對象實例。
  2. OnReadyToFly()時, BirdAirplane需要向IFlyBehavior實例詢問Fly()
  3. OnReadyToLand()時, BirdAirplane需要向IFlyBehavior實例詢問Land()
  4. OnReadyToFly()OnReadyToLand()是私有的。
  5. Bird繼承了AnimalAirplane繼承了PeopleMover

現在,假設我們稍后添加了MothHotAirBalloon和其他16個對象,讓我們說它們都遵循相同的模式。

我們現在需要20份以下代碼:

private IFlyBehavior _flyBehavior;

private void OnReadyToFly()
{
    _flyBehavior.Fly();
}

private void OnReadyToLand()
{
    _flyBehavior.Land();
}

我不喜歡這兩件事:

  1. 它不是很干(相同的九行代碼一遍又一遍地重復)。 如果我們發現了一個錯誤或將BankRight()添加到IFlyBehavior ,我們需要將更改傳播到所有20個類。

  2. 沒有任何方法可以強制所有20個類一致地實現這種重復的內部邏輯。 我們不能使用接口,因為接口只允許公共成員。 我們不能使用抽象基類,因為對象已經繼承了基類,並且C#不允許多重繼承(即使這些類還沒有繼承類,我們以后可能希望添加一個實現的新行為,比如ICrashable ,所以抽象基類並不總是一個可行的解決方案。

如果...?

如果C#有一個新的構造,比如patterntemplate或[在這里填寫你的想法],它就像一個接口,但允許你在成員上放置私有或受保護的訪問修飾符怎么辦? 您仍然需要為每個類提供一個實現,但是如果您的類實現了PFlyable模式,那么您至少應該有一種方法來強制每個類都有必要的樣板代碼來調用Fly()Land() 而且,使用像Visual Studio這樣的現代IDE,您將能夠使用“實現模式”命令自動生成代碼。

就個人而言,我認為只是擴展界面的含義以涵蓋任何合同,無論是內部(私人/受保護)還是外部(公共)更有意義,但我建議首先添加一個全新的結構,因為人們似乎非常堅定關於“界面”一詞的含義,我不希望語​​義成為人們答案的焦點。

問題:

無論你怎么稱呼它,我想知道我在這里建議的功能是否有意義。 我們是否需要某種方法來處理我們無法抽象出我們想要的代碼的情況,因為需要限制性訪問修飾符或者程序員無法控制的原因?

更新

根據AakashM的評論,我相信我要求的功能已有一個名稱:一個Mixin 所以,我想我的問題可以簡化為:“C#應該允許Mixins嗎?”

您描述的問題可以使用訪問者模式解決(一切都可以使用訪問者模式解決,所以要小心!)

訪問者模式允許您將實現邏輯移向新類。 這樣,您不需要基類,並且訪問者在不同的繼承樹上工作得非常好。

總結一下:

  1. 不需要將新功能添加到所有不同類型
  2. 對訪問者的調用可以拉到每個類層次結構的根目錄

有關參考,請參閱訪客模式

Visual Studio已經在“窮人形式”中提供了代碼片段。 此外,使用重構工具la ReSharper(甚至可能是Visual Studio中的本機重構支持),您可以在確保一致性方面獲得很長的成功。

[編輯:我沒有想到擴展方法,這種方法更進一步(你只需要將_flyBehaviour保持為私有變量)。 這使我的答案的其余部分可能已經過時了......]

然而; 只是為了討論:如何改進? 這是我的建議。

可以想象未來版本的C#編譯器將支持以下內容:

// keyword 'pattern' marks the code as eligible for inclusion in other classes
pattern WithFlyBehaviour
{
   private IFlyBehavior_flyBehavior;

   private void OnReadyToFly()
   {
      _flyBehavior.Fly();
   }

   [patternmethod]
   private void OnReadyToLand()
   {
      _flyBehavior.Land();
   }     
}

您可以使用以下內容:

 // probably the attribute syntax can not be reused here, but you get the point
 [UsePattern(FlyBehaviour)] 
 class FlyingAnimal
 {
   public void SetReadyToFly(bool ready)
   {
      _readyToFly = ready;
      if (ready) OnReadyToFly(); // OnReadyToFly() callable, although not explicitly present in FlyingAnimal
   }

 }

這會有所改善嗎? 大概。 是不是真的值得嗎? 也許...

我們不能使用擴展方法

    public static void OnReadyToFly(this IFlyBehavior flyBehavior)
    {
          _flyBehavior.Fly()
    }

這模仿了你想要的功能(或Mixins)

您剛剛描述了面向方面的編程

C#的一個流行的AOP實現似乎是PostSharp (主站點似乎已關閉/不適合我, 但這是直接的“關於”頁面)。


跟進評論:我不確定PostSharp是否支持它,但我認為你在談論AOP的這一部分

類型間聲明提供了一種表達影響模塊結構的橫切關注點的方法。 也稱為開放類,這使程序員能夠在一個地方聲明另一個類的成員或父母,通常是為了在一個方面組合與關注點相關的所有代碼。

我將使用擴展方法來實現代碼顯示的行為。

  1. BirdPlane對象實現了產權IFlyBehavior對象的接口IFlyer

     public interface IFlyer { public IFlyBehavior FlyBehavior } public Bird : IFlyer { public IFlyBehaviour FlyBehavior {get;set;} } public Airplane : IFlyer { public IFlyBehaviour FlyBehavior {get;set;} } 
  2. IFlyer創建擴展類

     public IFlyerExtensions { public void OnReadyToFly(this IFlyer flyer) { flyer.FlyBehavior.Fly(); } public void OnReadyToLand(this IFlyer flyer) { flyer.FlyBehavior.Land(); } } 

你能通過在.NET 4.0中使用新的ExpandoObject來獲得這種行為嗎?

開發Scala特征是為了解決這種情況。 還有一些研究在C#中包含特征

更新:我創建了自己的實驗, 在C#中擁有角色 看一看。

暫無
暫無

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

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