2010-06-25 91 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