2010-06-25 150 views
6

我正在尋找方法來改進我正在處理的應用程序中的一些代碼的一致性,簡潔性和可讀性。起始代碼看起來像這樣:帶有泛型類型參數的C#擴展方法

context.GetGraphType<Bar>().Subscribe<Fizz>(
    (instance, evt) => e.Execute((Bar)instance.Instance) 
); 

有許多幾乎與上面相同的代碼行。我想改寫它看起來是這樣的:

typeof(Bar).SubscribeTo<Fizz>(context); 

一方面,這將使我採取什麼正規化已經成爲一種非正式會議的優勢。另外,我希望它現在可以閱讀「bar在特定上下文中訂閱fizz事件」的內容,而不是「上下文獲取條形式並訂閱嘶嘶聲,然後執行某些操作」。我認爲流程是更好,我問過同事的同事。

我開始實施這個作爲擴展方法。爲了實現上述目的,我想使用抽象的泛型基類作爲事件類型,所以Fizz應該是Event<T>。這將意味着擴展方法的泛型類型參數必須被約束爲擴展方法被調用的類型。因此,對於上述示例,Fizz必須是Event<Bar>

這可能嗎?在此期間,我採取了另一種解決方案,但我仍然很好奇它是否可以實現。其他建議也歡迎。

謝謝!

編輯#1:要清楚,我意識到我可以使用一個額外的類型參數,但我正在尋找方法來避免這種情況。

編輯#2: 我想我會接受接受答案的細微變化,因爲它與我的場景不匹配100%。底線是可以使用泛型靜態類來代替Type的擴展方法來實現我的目標。謝謝dss539!

更新代碼(有可能是拼寫錯誤,因爲我在飛行中這樣做):

public class Bar { } 

public class Event<TSubscriber> 
{ 
    public abstract void Execute(TSubscriber source); 
} 

public class Fizz : Event<Bar> 
{ 
    public override void Execute(Bar bar) 
    { 
     // respond to event 
    } 
} 

public class Context { } 

public static class ForType<TSubscriber> 
{ 
    public static void SubscribeTo<TEvent>(Context context) 
     where TEvent : Event<TSubscriber> 
    { 
     context.GetType<TSubscriber>().Subscribe<TEvent>(
      (evt, args) => evt.Execute((TSubscriber)args.Source)); 
    } 
} 

public static void Run() 
{ 
    ForType<Bar>.SubscribeTo<Fizz>(context); 
} 
+1

我不太明白你的問題。你現有的方法簽名是什麼樣的? '訂閱(這種類型,動作)'?如果你顯示你有什麼(或相當的),這可能有助於解釋。 – dss539 2010-06-25 14:48:08

+0

我想我有一個類似的設計問題。祝你好運:) – leppie 2010-06-25 14:48:30

+0

@ dss539 它會更像訂閱(這種類型,上下文ctx)。問題是,我沒有辦法(我知道)將T約束爲Event 類型。 – 2010-06-25 15:07:11

回答

6

這不正是像你這樣問,但也許就足夠了。

internal class Program 
{ 
    static void Main(string[] args) 
    { 
     var fizzHandler = new Fizz(); 
     var context = new Context(); 
     Handle<Bar>.With(fizzHandler, context); 
    } 
} 
public class Bar { } 
public class Event<T> { } 
public class Fizz : Event<Bar> { } 
public class Context { }; 
public static class Handle<T> 
{ 
    public static void With(Event<T> e, Context c) 
    { 
     //do your stuff 
    } 
} 
+0

這正是我正在尋找的。它使我能夠完成我的最初可讀性目標,而無需繞過編譯器。謝謝! – 2010-07-01 19:48:50

+0

@布賴恩 - 很高興我能幫忙:) – dss539 2010-07-07 17:55:26

4

爲什麼不能做一些更idomatic,在那裏你可以使用通用的約束來執行規則:

public static class SubscriptionManager 
{ 
    public static void SubsribeTo<TSub,TEvent>(Context context) 
     where TEvent : Event<TSub> 
    { 
     /// you code... 
    } 
} 

的通話將如下所示:

SubscriptionManager.SubsribeTo<Bar,Fizz>(context); 

約束where TEvent : Event<TSub>確保您所需的事件和訂閱類型之間的關係。在我的書中,還可以選擇Type這個類的擴展方法 - 因爲這會導致intellisense混亂。在許多情況下使用Type,並且在所有Type的實例上出現Intellisense中的虛假方法往往會令人困惑。對於圖書館的消費者來說,這是「訂閱」的方式 - 除非他們真的看到了它的代碼示例。

+0

感謝您的回答!我意識到可以用這種方式強制約束(參見上面的編輯)。您的建議接近我考慮的建議,但首選擴展方法。我明白反對在Type這樣的類上使用擴展方法,但該方法本地化爲處理訂閱的單個類。另外,這不是一個會被其他人使用的圖書館方法。 – 2010-06-25 15:04:10

0

你或許可以得到接近延伸System.Type(有typeof(T).)和添加(擴展)方法的上下文是一種把.NET類型內部表示類型(同由GetGraphType返回)。

static class Ext { 

    public static TypeofTypeofBar GetGraphTypeFromDotNetType(this Context ctx, Type t) { 
     return __something(t); 
    } 

    public static void SubscribeTo<F, E>(this Type type, Context ctx, E e) 
     where E: Event<T> { 
     context.GetGraphTypeFromDotNetType(type).Subscribe<F>(a); 
    } 

} 

...

typeof(Bar).SubscribeTo(context, action); 
+0

嘿茂,剛纔你所描述的方法已經存在。我對你的例子中的類型參數有點困惑。你的意思是「哪裏E:事件」?如果是這種情況,我不需要傳遞「事件」參數,類型就足夠了(實際上我不能提供事件實例)。但是,似乎用戶的類型仍然需要作爲類型參數提供。所以,你基本上最終會得到「typeof(Bar).SubscribeTo (ctx)」,這是另一個我不感興趣的變體。還是)感謝你的建議! – 2010-06-25 15:16:38