C# 中具有强类型发送者的 EventHandler

发布于 2024-12-09 03:20:12 字数 1707 浏览 0 评论 0原文

让我烦恼的一件事是,在默认事件中,发送者是对象类型,因此几乎总是需要手动转换才能使用它。幸运的是,由于 VB 现在也支持委托的差异,我们可以通过发送者强类型化的方式更新事件的签名,请参阅:事件参数; “发送者作为对象”,还是“发送者作为T”?

不幸的是,这对于发送者类型为对象的现有声明事件不起作用。

现在,一种解决方案当然是生成一个假的 EventHandler,它在内部为您处理转换。我做了一个简单的例子,请参阅:

struct EventHandler<TSender, TEventArgs>
    where TEventArgs: EventArgs
{
    private readonly Action<TSender, TEventArgs> _delegate;

    public EventHandler(Action<TSender, TEventArgs> @delegate)
    {
        if (@delegate == null)
            throw new ArgumentNullException("@delegate");

        _delegate = @delegate;
    }

    public static implicit operator EventHandler<TEventArgs>(EventHandler<TSender, TEventArgs> eventHandler)
    {
        return new EventHandler<TEventArgs>(eventHandler.Execute);
    }

    private void Execute(object sender, EventArgs e)
    {
        TSender typedSender = (TSender)sender;
        TEventArgs typedEventArgs = (TEventArgs)e;

        _delegate(typedSender, typedEventArgs);
    }
}

可以像您期望的那样使用它:

class Program
{
    event EventHandler<EventArgs> Test;

    static void Main(string[] args)
    {
        new Program().Main();
    }

    void Main()
    {
        Test += new EventHandler<Program, EventArgs>(TestEventHandler);

        Test(this, EventArgs.Empty);
    }

    void TestEventHandler(Program sender, EventArgs e)
    {
        throw new NotImplementedException();
    }
}

现在,如果我真的想使用它,还有很多工作要做。 (该结构的行为应该与原始委托一样)。但我确实有一种感觉,要么已经有一个很好的实现,要么没有实现,因为我忽略了一些主要缺点。

谁能回答我上面的问题?还有其他提示吗?

One thing that annoys me is that in a default Event, Sender is of type object and therefore almost always requires a manual cast before we can use it. Luckily since VB now also supports variance in delegates, we can update the signature of an event in such a way that the sender is strongly typed, see: Event parameter; "sender as Object", or "sender as T"?

Unfortunatly this doesn't work for existing declared events which senders are of type object.

Now one solution would be ofcourse to generate a fake EventHandler which internally takes care of the cast for you. I made a quick example, see:

struct EventHandler<TSender, TEventArgs>
    where TEventArgs: EventArgs
{
    private readonly Action<TSender, TEventArgs> _delegate;

    public EventHandler(Action<TSender, TEventArgs> @delegate)
    {
        if (@delegate == null)
            throw new ArgumentNullException("@delegate");

        _delegate = @delegate;
    }

    public static implicit operator EventHandler<TEventArgs>(EventHandler<TSender, TEventArgs> eventHandler)
    {
        return new EventHandler<TEventArgs>(eventHandler.Execute);
    }

    private void Execute(object sender, EventArgs e)
    {
        TSender typedSender = (TSender)sender;
        TEventArgs typedEventArgs = (TEventArgs)e;

        _delegate(typedSender, typedEventArgs);
    }
}

which can be used as you would expect it to be used:

class Program
{
    event EventHandler<EventArgs> Test;

    static void Main(string[] args)
    {
        new Program().Main();
    }

    void Main()
    {
        Test += new EventHandler<Program, EventArgs>(TestEventHandler);

        Test(this, EventArgs.Empty);
    }

    void TestEventHandler(Program sender, EventArgs e)
    {
        throw new NotImplementedException();
    }
}

Now if I really want to use this, there is alot of work to be done. (The struct should behave just like the original delegate). Yet i do have the feeling that there is either already a great implementation out there, or there is no implementation since there are some major drawbacks i overlooked.

Who can answer me the above question? Any other hints?

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

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

发布评论

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

评论(2

唠甜嗑 2024-12-16 03:20:12

我想不出任何比它的代码更少的解决方案。

var original = (OriginalType)sender;

另外,如果类是你的,没有什么可以阻止你创建自己的委托而不是 EventHandler 委托。

delegate void EventHandler<in TSender, in TArgs>(TSender sender, TArgs args);

这在 args 和 sender 中是逆变的。

人们通常还拥有对象的引用,该对象的对象是事件被引发,因此他们很少需要从发件人那里获取信息。在你的情况下有可能吗?

编辑:既然您提到您正在处理不是由您编写的事件,所以修改委托是不可能的,并且您可能没有对该对象的引用,因此您必须求助于在这种情况下的发件人。现在,在您的解决方案中,您无法取消订阅该事件。如果您修改它以支持这一点,那么您将必须保留对您的这个结构的引用,这比简单的转换需要更多的工作。我认为铸造仍然是最干净的解决方案。

I can't think of any solution that would have less code than

var original = (OriginalType)sender;

Also if class is yours nothing stops you from creating your own delegate instead of EventHandler delegate

delegate void EventHandler<in TSender, in TArgs>(TSender sender, TArgs args);

This is contravariant in args and sender

People usually also have the reference of the object for which the event is raised and therefore they rarely need to get the thing out of the sender. Could it be possible in your case?

Edit: Since you mentioned you're dealing with events that are not written by you so modifying the delegate is out of question and you probably don't have reference to the object so you have to resort to the sender in that case. Now in your solution you can not unsubscribe to the event. If you modify it to support that then you'll have to keep the reference to this struct of yours which is more work than simple casting. I think casting is still the cleanest solution.

薄暮涼年 2024-12-16 03:20:12

我认为使用方法作为事件处理程序现在是不好的 OOP 实践。现在我们有了匿名委托,我们不需要创建方法来处理事件。创建一个可以被类中所有其他方法调用的事件处理程序方法非常类似于公开类成员并让任何类调用任何内容。

过去我们必须这样写:

public class Program
{
    public void Main()
    {
        var button = new Button();
        button.Click += button_Click;
    }

    void button_Click(object sender, EventArgs e)
    {
        var button = (Button)sender; //Need to cast here
    }
}

但现在我们可以这样写:

public class Program
{
    public void Main()
    {
        var button = new Button();
        button.Click += (sender, e) =>
        {
            //Can use `button` here
            //Just ignore `sender`
        };
    }
}

使用匿名委托允许我们直接使用事件“发送者”引用,而不需要任何转换。

更加干净的面向对象编码。

I'm of the opinion that using methods as event handlers is now bad OOP practice. Now that we have anonymous delegates we just don't need to make methods just to handle events. Creating an event handler method that could get called by every other method in a class is much like making class members public and letting any class call anything.

In the past we have had to write this:

public class Program
{
    public void Main()
    {
        var button = new Button();
        button.Click += button_Click;
    }

    void button_Click(object sender, EventArgs e)
    {
        var button = (Button)sender; //Need to cast here
    }
}

But now we can write this:

public class Program
{
    public void Main()
    {
        var button = new Button();
        button.Click += (sender, e) =>
        {
            //Can use `button` here
            //Just ignore `sender`
        };
    }
}

Using anonymous delegates allows us to use the event "sender" reference directly without the need of any casting.

Much cleaner object oriented coding.

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