使用泛型类型参数对类型进行 C# 扩展方法

发布于 2024-09-07 09:42:09 字数 1583 浏览 8 评论 0原文

我正在寻找提高我正在开发的应用程序中某些代码的一致性、简洁性和可读性的方法。起始代码看起来像这样:

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

有许多与上面几乎相同的代码行。我想将其重写为如下所示:

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

一方面,这将使我能够利用已经成为非正式约定的形式。另外,我希望它现在会读成“bar 在给定上下文中订阅 fizz 事件”,而不是“上下文获取 bar 类型并订阅 fizz,然后执行一些操作”。我认为流程更好,我询问的同事也同意。

我开始将其作为扩展方法来实现。为了完成上述任务,我想使用事件类型的抽象通用基类,因此 Fizz 将是 Event。这意味着扩展方法的泛型类型参数必须被限制为调用扩展方法的类型。因此,对于上面的示例,Fizz 必须是 Event 类型。

这可能吗?与此同时,我采用了替代解决方案,但我仍然很好奇它是否可以实现。也欢迎其他建议。

谢谢!

编辑#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);
}

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.

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

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.

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. The bottom line is that a generic static class can be used instead of an extension method of Type to accomplish my goal. Thanks 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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

逆光飞翔i 2024-09-14 09:42:09

这并不完全像你问的那样,但也许就足够了。

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

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
    }
}
黑寡妇 2024-09-14 09:42:09

为什么不做一些更自动化的事情,您可以使用通用约束来强制执行规则:

 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 确保事件和订阅之间的关系键入您想要的内容。在我的书中,它也比类 Type 上的扩展方法更可取 - 因为这往往会使智能感知变得混乱。 Type 在许多情况下都会使用,并且在所有 Type 实例上的 Intellisense 中出现虚假方法往往会造成混乱。对于图书馆的消费者来说,这是“订阅”的方式也是不明显的 - 除非他们实际上已经看到了它的代码示例。

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. It's also preferrable in my book to an extension method on the class Type - because that tends to clutter intellisense. Type is used in many situations, and having spurious methods appear in Intellisense on all instances of Type tends to be confusing. 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.

往事随风而去 2024-09-14 09:42:09

您可能可以接近扩展 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);

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).

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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文