C# 使用泛型类型动态创建的空事件委托

C# 使用泛型类型动态创建的空事件委托,c#,events,generics,delegates,postsharp,C#,Events,Generics,Delegates,Postsharp,我从这里复制了代码: 但是,当事件位于泛型类中时,我似乎无法让它工作。我有一个类定义为: Public Class MultiKeyDictionary<TFirstKey, TSecondKey, TValue> 但是初始化代码异常抱怨类型的ContainsGenericParameters设置为true(或类似设置) 我已将RuntimeInitialize方法中该链接中的代码更改为: public override void RuntimeInitialize(EventIn

我从这里复制了代码:

但是,当事件位于泛型类中时,我似乎无法让它工作。我有一个类定义为:

Public Class MultiKeyDictionary<TFirstKey, TSecondKey, TValue>
但是初始化代码异常抱怨类型的ContainsGenericParameters设置为true(或类似设置)

我已将RuntimeInitialize方法中该链接中的代码更改为:

public override void RuntimeInitialize(EventInfo eventInfo) {
    base.RuntimeInitialize(eventInfo);
    Type eventType;
    MethodInfo delegateInfo = eventInfo.EventHandlerType.MethodInfoFromDelegateType();          
    ParameterExpression[] parameters = delegateInfo.GetParameters().Select(p => Expression.Parameter(p.ParameterType)).ToArray();
    if(eventInfo.EventHandlerType.ContainsGenericParameters) {
        var genericDelegate = eventInfo.EventHandlerType.GetGenericTypeDefinition();
        var genericParams = genericDelegate.GetGenericArguments();
        eventType = genericDelegate.MakeGenericType(genericParams);
    } else {
        eventType = eventInfo.EventHandlerType;
    }
    Delegate emptyDelegate = Expression.Lambda(eventType, Expression.Empty(), "EmptyDelegate", true, parameters).Compile();
    this.addEmptyEventHandler = instance => eventInfo.AddEventHandler(instance, emptyDelegate);
}

但是我现在得到的只是一个ArgumentException:在创建emptyDelegate的行中,类型为“TValue”的ParameterExpression不能用于类型为“TValue”的委托参数。

正如我在我的博客上早些时候回答的,这里的主要问题是
RuntimeInitialize()的时间
被称为PostSharp,但它还不知道该类将使用哪些泛型参数初始化。但是,当调用OnConstructorEntry()时,我们确实有此信息。有关PostSharp方面如何工作的更多信息,请务必阅读

在现有代码中,当在运行时为类创建方面时(
RuntimeInitialize()
方法),我只是忽略了类可以是泛型的事实。这是我的疏忽。无法使用
Expression.Lambda
编译“泛型”类型,因此无法编译通用事件处理程序,该处理程序可由泛型类型的所有不同实例化使用

您需要在运行时分别为泛型类型的每个不同实例化编译此空事件处理程序。这可以在
OnConstructorEntry
中完成,您可以从PostSharp传递的
MethodExecutionArgs
参数接收实例类型

为了知道需要向哪个事件添加处理程序,需要在运行时初始化时将
EventInfo
存储在aspect中

[NonSerialized]
EventInfo _event;
通过比较名称,您可以知道特性应用于哪个事件。以下内容是ConstructorEntry()上的当前工作代码

这仍然留下了一个悬而未决的问题。我们不希望每次构造类型时都进行所有这些反射!因此,理想情况下,您应该像前面在
RuntimeInitialize()
中那样缓存添加空处理程序的方法。由于方面代码由泛型类型的所有实例化“共享”(它们使用相同的作用域),因此应该分别缓存每个实例类型。例如,使用
字典
,其中
类型
指实例类型,
操作
是可以将空事件处理程序添加到实例的方法


这正是我现在在库中使用的实现。正如您将看到的,我使用了一个
CachedDictionary
类,它已经为您处理了大部分缓存逻辑,因为这是一种常见的场景。现在成功了。

我正在讨论这个问题。有趣的是,我在寻找帮助时发现了这篇文章。:)
[NonSerialized]
EventInfo _event;
Type runtimeType = args.Instance.GetType();
EventInfo runtimeEvent =
    runtimeType.GetEvents().Where( e => e.Name == _event.Name ).First();

MethodInfo delegateInfo =
    DelegateHelper.MethodInfoFromDelegateType( runtimeEvent.EventHandlerType );
ParameterExpression[] parameters = delegateInfo
    .GetParameters()
    .Select( p => Expression.Parameter( p.ParameterType ) )
    .ToArray();
Delegate emptyDelegate = Expression.Lambda(
    runtimeEvent.EventHandlerType, Expression.Empty(),
    "EmptyDelegate", true, parameters ).Compile();

// Add the empty handler to the instance.
MethodInfo addMethod = runtimeEvent.GetAddMethod( true );
if ( addMethod.IsPublic )
{
    runtimeEvent.AddEventHandler( args.Instance, emptyDelegate );
}
else
{
    addMethod.Invoke( args.Instance, new object[] { emptyDelegate } );
}