【问题标题】:Attaching an event handler through reflexion通过反射附加事件处理程序
【发布时间】:2014-04-24 21:44:02
【问题描述】:

我有以下代码:

namespace ConsoleApplication
{
        static void Main(string[] args)
        {
            Device device = new Device();
            device.Command += new EventHandler<DeviceSpecialArgs>(device_Command);
        }

        public static void device_Command(Object source, DeviceSpecialArgs args)
        {
            Console.WriteLine("Command: {0}, Reguest: {1}", args.Command, args.Request);
        }
    }
}

我必须做完全相同的事情,但需要在运行时加载包含类型 Device 和 DeviceSpecialArgs 的程序集。我知道如何使用反射加载程序集,但我发现事件处理部分令人费解:

namespace ConsoleApplication
{
    static void Main(string[] args)
    {
        // Load the assembly
        string dllPath = @"C:\Temp\Device.dll"
        Assembly asm = Assembly.LoadFrom(dllPath);

        // Instanciate Device
        Type deviceType = asm.GetType("Device");
        object device = Activator.CreateInstance(deviceType);

        // How do I subscribe to the Command event?
    }

    // args would normally be a DeviceSpecialArgs but since that type is 
    // unknown at compile time, how do I prototype the handler?
    public static void device_Command(Object source, ??? args)
    {
        Console.WriteLine("Command: {0}, Reguest: {1}", args.Command, args.Request);
    }   
}

如何使用反射订阅事件?另外,由于“args”的类型在编译时未知,我应该如何对处理程序本身进行原型设计?仅供参考,我是 C# 3 和 .NET 3.5。

【问题讨论】:

  • 看看这个帖子here。似乎是重复的。
  • 此处强制执行类型安全,您必须使用类型与委托类型兼容的 e 参数编写事件处理程序。你得到的只是object。然后,您还需要对 e.GetType() 进行反思以挖掘出您需要的属性。

标签: c# reflection event-handling .net-3.5 c#-3.0


【解决方案1】:

首先,查看MAF

另一种方法是向第一个程序集添加对第二个程序集的引用。然后在第二个程序集中创建几个接口,并在第一个程序集中创建类来实现它们:

public interface IDeviceSpecialArgs
{
    string Command { get; }
    string Request { get; }
}

public interface IDevice
{
    event EventHandler<IDeviceSpecialArgs> Command;
}

第一次大会:

public sealed class DeviceSpecialArgs : EventArgs, IDeviceSpecialArgs
{
    private readonly string command;
    private readonly string request;

    public string Command
    {
        get { return command; }
    }

    public string Request
    {
        get { return request; }
    }

    public DeviceSpecialArgs(string command, string request)
    {
        this.command = command;
        this.request = request;
    }
}

public class Device : IDevice
{
    public event EventHandler<IDeviceSpecialArgs> Command;

    ...
}

在第二个程序集中,只需将新实例化的对象转换为相应的接口:

IDevice device = Activator.CreateInstance(deviceType) as IDevice;

现在您可以订阅Command 事件,因为它是在IDevice 接口中声明的:

device.Command += new EventHandler<IDeviceSpecialArgs>(device_Command);

编辑:如果您无法控制正在加载的程序集,请尝试以下代码。它只是使用第二个参数的EventArgs 类型创建一个处理程序,并使用反射来获取其属性:

internal class DeviceEvent
{
    private readonly Type deviceType;
    private readonly Type deviceSpecialArgsType;

    public DeviceEvent()
    {
        // Load the assembly
        const string dllPath = @"C:\Temp\Device.dll";
        Assembly asm = Assembly.LoadFrom(dllPath);

        // Get types
        deviceType = asm.GetType("Device");
        deviceSpecialArgsType = asm.GetType("DeviceSpecialArgs");

        // Instantiate Device
        object device = Activator.CreateInstance(deviceType);
        // Subscribe to the Command event
        deviceType.GetEvent("Command").AddEventHandler(device, (Delegate.CreateDelegate(typeof(EventHandler), GetType().GetMethod("Device_Command", BindingFlags.NonPublic))));
    }

    private void Device_Command(object sender, EventArgs e)
    {
        string command = deviceSpecialArgsType.GetProperty("Command", BindingFlags.Public).GetValue(e, null).ToString();
        string request = deviceSpecialArgsType.GetProperty("Request", BindingFlags.Public).GetValue(e, null).ToString();
        ...
    }
}

【讨论】:

  • 谢谢,但不幸的是,我无法控制我正在加载的程序集(它由第三方提供)。此外,在这种情况下,无法将其添加为参考。反射是唯一的选择。
  • 我已经更新了我的答案。请注意,它未经测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多