在C#中使用强类型发件人的EventHandler

时间:2011-10-10 06:06:26

标签: c# .net event-handling

令我恼火的一件事是,在默认事件中,Sender属于object类型,因此在我们使用它之前几乎总是需要手动转换。幸运的是,由于VB现在也支持代理的差异,我们可以通过强类型发送者的方式更新事件的签名,请参阅:Event parameter; "sender as Object", or "sender as T"?

不幸的是,这对于发件人属于object类型的现有声明事件不起作用。

现在一个解决方案是生成一个假的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();
    }
}

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

谁可以回答我上述问题?还有其他提示吗?

2 个答案:

答案 0 :(得分:2)

我想不出任何代码比

少的解决方案
var original = (OriginalType)sender;

此外,如果您的课程是您的,那么没有什么可以阻止您创建自己的委托而不是EventHandler委托

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

这在args和发件人中是逆变的

人们通常也会引用引发事件的对象,因此很少需要将事物从发件人那里拿出来。在你的情况下可以吗?

编辑:由于您提到您正在处理非您编写的事件,因此修改代理是不可能的,您可能没有引用该对象,因此您必须在这种情况下诉诸发件人。现在,在您的解决方案中,您无法取消订阅该活动。如果你修改它以支持它,那么你将不得不保留对你的这个结构的引用,这比简单的转换更有用。我认为铸造仍然是最干净的解决方案。

答案 1 :(得分:-2)

我认为使用方法作为事件处理程序现在是错误的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`
        };
    }
}

使用匿名委托允许我们直接使用事件“sender”引用,而无需任何转换。

更清晰的面向对象编码。