简体   繁体   English

具有泛型类型参数的类型的C#扩展方法

[英]C# Extension Method on Type With Generic Type Argument

I'm looking at ways to improve the consistency, brevity, and readability of some code in the application I'm working on. 我正在研究如何提高我正在处理的应用程序中某些代码的一致性,简洁性和可读性。 The starting code looked something like this: 起始代码看起来像这样:

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

There are a number of nearly identical lines of code like the above. 如上所述,有许多几乎相同的代码行。 I wanted to rewrite it to look something like this: 我想重写它看起来像这样:

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

For one thing, this would allow me to take advantage of formalizing what had already become an informal convention. 首先,这将使我能够利用已经成为非正式会议的形式化。 Also, I hoped that it would now read something like “bar subscribes to the fizz event on the given context”, rather than “the context gets the bar type and subscribes to fizz and then does some stuff.” I think that the flow is better, and the coworker I asked about it agreed. 此外,我希望它现在会读取类似“bar在订阅上下文中订阅fizz事件”,而不是“上下文获取条形码并订阅fizz然后做一些事情。”我认为流程是更好,我询问同事的同事。

I started to implement this as an extension method. 我开始将其作为扩展方法实现。 In order to accomplish the above I wanted to make use of an abstract generic base class for the event type, so Fizz would be Event<T> . 为了实现上述目的,我想为事件类型使用抽象通用基类,因此Fizz将是Event<T> This would mean that the generic type argument to the extension method would have to be constrained to be of the type that the extension method is called for. 这意味着扩展方法的泛型类型参数必须被约束为调用扩展方法的类型。 So, for the above example, Fizz would have to be of type Event<Bar> . 因此,对于上面的示例, Fizz必须是Event<Bar>类型。

Is this possible? 这可能吗? I went with an alternative solution in the mean time, but I am still curious if it can be accomplished. 我在同一时间采用了替代解决方案,但我仍然很好奇是否可以实现。 Other suggestions are welcome as well. 其他建议也是受欢迎的。

Thanks! 谢谢!

Edit #1: Just to be clear, I realize that I could use an additional type parameter, but I'm looking for ways to avoid that if possible. 编辑#1:为了清楚,我意识到我可以使用一个额外的类型参数,但我正在寻找避免这种情况的方法。

Edit #2: I think I'm going to go with a slight variation of the accepted answer, since it doesn't match up 100% with my scenario. 编辑#2:我想我会接受一个接受答案的微小变化,因为它与我的情景不符合100%。 The bottom line is that a generic static class can be used instead of an extension method of Type to accomplish my goal. 底线是可以使用通用静态类而不是Type的扩展方法来实现我的目标。 Thanks dss539! 谢谢dss539!

Update code (there may be typos since I'm doing this on the fly): 更新代码(因为我在飞行中这样做可能会出现错别字):

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

This is not exactly like you asked, but maybe it will suffice. 这不完全像你问的那样,但也许就足够了。

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

Why not do something a bit more idomatic, where you could use generic constraints to enforce the rules: 为什么不做一些更具体味的事情,你可以使用通用约束来强制执行规则:

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

The calls would look like: 这些电话看起来像:

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

The constraint where TEvent : Event<TSub> ensures the relationship between the event and subscription type that you desire. where TEvent : Event<TSub>的约束确保了您希望的事件和订阅类型之间的关系。 It's also preferrable in my book to an extension method on the class Type - because that tends to clutter intellisense. 在我的书中,对于类Type的扩展方法也是优选的 - 因为这往往会使智能感知混乱。 Type is used in many situations, and having spurious methods appear in Intellisense on all instances of Type tends to be confusing. 在许多情况下使用Type ,并且在所有Type实例上在Intellisense中出现虚假方法往往会造成混淆。 It's also non-obvious for consumers of library that this is the way to "subscribe" - unless they've actually seen a code example of it. 对于图书馆的消费者而言,这是“订阅”的方式也是不明显的 - 除非他们实际上已经看到了它的代码示例。

You can probably get close extending System.Type (to have typeof(T). ) and adding an (extension) method to context that transforms a .NET type to your internal type representation (same as returned by GetGraphType ). 您可以近距离扩展System.Type (具有typeof(T). )并向上下文添加(扩展)方法,将.NET类型转换为内部类型表示(与GetGraphType返回的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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM