目录

泛型方法

静态泛型方法

实例泛型方法

事件

事件添加访问器

事件删除访问者

总结


现在是时候介绍以下成员了。

  1. 泛型方法
  2. 事件

泛型方法

以前我们没有介绍C#关于方法的非常重要的特性。如果您阅读本节的标题,您已经知道我在说什么。在C#中,可以编写泛型方法,这些方法没有刚性参数类型或刚性返回类型,因为它们可能会有所不同并且取决于类型参数。大多数DelegateFactory类方法都是泛型的。我们如何通过委托调用这些方法?主要问题是我们不能,或者至少不那么容易。实际上,具有不同类型参数集的泛型方法的每个版本都是不同的方法,并且我们需要针对该组参数分配不同的委托。

静态泛型方法

考虑TestClass中的以下方法。

public static T StaticGenericMethod<T>() where T : new ()
{return new T();
}public static T StaticGenericMethod<T>(T param)
{return param;
}

第一个是非常简单的没有参数的泛型方法,返回类型是来自类型参数的。C#世界中有很多类型都有默认的无参数构造函数,所以我们可以使用它并创建非常基本的泛型方法,实际上有意义的方法。第二个甚至更简单(它只返回类型参数给出的相同类型的对象),但重要的是显示采用不同的参数集的泛型方法的重载。

两者都是静态方法,所以我们可以使用StaticMethod方法,对吗?是的,我们可以,而它将无法工作,因为我们无法为第一个StaticGenericMethod示例(它将抛出错误,因为传递的委托类型不兼容)创建正确的委托或者如果至少有一个参数是不同的类型时将返回null(如果我们不知道方法重载的参数,就不能通过它的参数来搜索,因为它们不同于类型参数)。这些问题的解决方案是新的重载,它将类型参数(或带有类型的新参数)应用于泛型方法。

使用委托的传递类型参数与索引器和方法(静态和实例)成员类型的问题相同。参数类型的数量确实没有限制(在ActionFunc类中,除了理由和16个限制),因此我们必须为每个类型参数编写不同的StaticMethodInstanceMethod重载。这没什么意义,根据经验,我可以说大多数方法都有一个或两个类型参数。我可能在某些方法中用了3参数,但我不记得了。这就是为什么(和DelegateFactory其他方法保持一致性)我认为,支持最多 3个类型参数就足够了。对于每种其他情况,类型参数的类型数组就足够了。

但首先我们需要获得泛型方法的方法信息,并考虑可能的重载。重载可以是参数和类型参数数量的任意组合。例如,如果它们与类型参数的数量不同,则可以有两个没有参数的重载。或者,您可以使用相同类型参数(或无)但具有不同参数类型的重载。考虑以下方法。

public static T StaticGenericMethod<T>() where T : new()
{return new T();
}public static T StaticGenericMethod<T>(T param)
{return param;
}public static T StaticGenericMethod<T>(T param, int i) where T : ITestInterface
{return param;
}public static T StaticGenericMethod<T>(T param, int i, bool p) where T : struct
{return param;
}public static T1 StaticGenericMethod<T1, T2>() where T1 : new()
{return new T1();
}public static T1 StaticGenericMethod<T1, T2, T3>(int i) where T1 : new()
{return new T1();
}

这些都是完全有效的方法,因为GetStaticMethod方法的早期版本不起作用。为什么?反射不允许我们轻松找到泛型方法重载,如果同一组参数(如果一个或多个方法是泛型的,这是完全可能的)有多个重载,则Type.GetMethod将抛出异常。如何解决这个问题?当存在多个重载时,我们可以捕获AmbiguousMatchException异常,并检查我们正在寻找的泛型方法的所有方法。为此,我们需要比较两对集合:我们正在寻找的第一组参数,并设置具有正确约束的pf类型参数。

private static MethodInfo GetStaticMethodInfo(Type source, string name, Type[] parametersTypes, Type[] typeParameters = null)
{MethodInfo methodInfo = null;try{methodInfo = (source.GetMethod(name, BindingFlags.Static, null, parametersTypes, null) ??source.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic, null, parametersTypes, null)) ??source.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, null, parametersTypes, null);}catch (AmbiguousMatchException){//swallow and test generics}//check for generic methodsif (typeParameters != null){var ms = source.GetMethods(BindingFlags.Static).Concat(source.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)).Concat(source.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static));foreach (var m in ms){if (m.Name == name && m.IsGenericMethod){var parameters = m.GetParameters();var genericArguments = m.GetGenericArguments();var parametersTypesValid = parameters.Length == parametersTypes.Length;parametersTypesValid &= genericArguments.Length == typeParameters.Length;if (!parametersTypesValid){continue;}for (var index = 0; index < parameters.Length; index++){var parameterInfo = parameters[index];var parameterType = parametersTypes[index];if (parameterInfo.ParameterType != parameterType&& parameterInfo.ParameterType.IsGenericParameter&& !parameterInfo.ParameterType.CanBeAssignedFrom(parameterType)){parametersTypesValid = false;break;}}for (var index = 0; index < genericArguments.Length; index++){var genericArgument = genericArguments[index];var typeParameter = typeParameters[index];if (!genericArgument.CanBeAssignedFrom(typeParameter)){parametersTypesValid = false;break;}}if (parametersTypesValid){methodInfo = m.MakeGenericMethod(typeParameters);break;}}}}return methodInfo;
}

这比以前复杂得多!但别担心,我会解释一切。

以前的代码在try语句中。第一个更改是带有一组类型参数的额外参数,应该针对方法泛型参数编号和所有约束的每个重载进行检查。如果我们确实寻找泛型方法(typeParameter不是空的),我们正在逐步转向寻找方法。在检索具有所有可见性的所有类型的方法之后,我们必须按名称过滤它们。然后我们检查方法是否泛型。如果是,我们必须检查参数数量和类型参数。如果这些是正确的,我们必须将它们与委托参数进行比较并在同一位置键入参数。检查参数并不像仅比较类型那么容易,因为参数可以具有类型参数的类型,类型参数可以具有约束。因此,我们必须检查参数的类型,如果它是不同的(泛型参数具有特殊的泛型类型),我们必须检查参数是否是泛型的,以及它的泛型类型是否可以表示我们正在寻找的参数类型。在示例in t类型可以由带有struct约束的泛型类型T的参数来表示。如果不了解IsAssignableFrom方法的细节,这可能有点难以理解。当然有Type.IsAssignableFrom方法,但奇怪的是它没有正常工作,因为没有约束的简单参数无法与任何类匹配。两者的代码是相同的,除了下面的检查代码,这导致错误的结果。

RuntimeType toType = this.UnderlyingSystemType as RuntimeType;
if (toType != null)return toType.IsAssignableFrom(c);

我不知道为什么,但在删除它并创建新方法而不使用它之后,它会正常工作,也就是说,不使用类型参数约束的方法中的T类型参数:

public static T StaticGenericMethod<T>(T param)

当我们检查IsAssignableFrom新实现时,可以从TestClass类型分配。

public static bool CanBeAssignedFrom(this Type destination, Type source)
{if (source == null || destination == null){return false;}if (destination == source || source.IsSubclassOf(destination)){return true;}if (destination.IsInterface){return source.ImplementsInterface(destination);}if (!destination.IsGenericParameter){return false;}var constraints = destination.GetGenericParameterConstraints();return constraints.All(t1 => t1.CanBeAssignedFrom(source));
}private static bool ImplementsInterface(this Type source, Type interfaceType)
{while (source != null){var interfaces = source.GetInterfaces();if (interfaces.Any(i => i == interfaceType|| i.ImplementsInterface(interfaceType))){return true;}source = source.BaseType;}return false;
}

以上方法必须在两种情况下使用。第一种,我们必须将委托的给定参数集与方法的参数进行比较。这是通过在Type.GetMethod方法中搜索方法自动完成的。但是现在,既然我们知道,有两个泛型方法可以使用相同的参数集,我们必须针对所需类型检查每个泛型方法是否重载。如果方法没有泛型参数比较很容易:只检查两个Type类型实例是否相等。但是如果方法具有泛型参数,我们必须比较此参数的类型参数约束。这是通过CanBeAssignedFrom方法完成的。

如果我们的参数集是正确的,我们必须以类似的方式比较类型参数。每个提供的具体类型都必须在同一索引及其约束条件下检查方法的类型参数。它也是由CanBeAssignedFrom方法完成的。

如果两个集合都是正确的,则意味着我们有所需的泛型方法重载,它可以用于通过Type.MakeGenericMethod生成具有具体类型的方法

好。让我们来介绍静态方法。第一种情况,当我们知道所有委托类型,然后知道未知类型(通过对象),之后我们将对实例方法做同样的事情。

泛型的第一个新的重载非常容易实现。由于我们已经有了GetStaticMethodInfo方法,它不仅搜索方法信息,而且还使用指定的类型参数创建非泛型方法,我们可以使用返回的对象来创建委托,就像非泛型委托一样。唯一的区别是委托方法的额外类型参数...类型参数。它在代码中看起来会更清晰。

public static TDelegate StaticMethod<TSource, TDelegate, TParam1>(string name)where TDelegate : class
{return typeof(TSource).StaticMethod<TDelegate>(name, typeof(TParam1));
}public static TDelegate StaticMethod<TSource, TDelegate, TParam1, TParam2>(string name)where TDelegate : class
{return typeof(TSource).StaticMethod<TDelegate>(name, typeof(TParam1), typeof(TParam2));
}public static TDelegate StaticMethod<TSource, TDelegate, TParam1, TParam2, TParam3>(string name)where TDelegate : class
{return typeof(TSource).StaticMethod<TDelegate>(name, typeof(TParam1), typeof(TParam2), typeof(TParam3));
}public static TDelegate StaticMethod<TSource, TDelegate>(string name, params Type[] typeParameters)where TDelegate : class
{return typeof(TSource).StaticMethod<TDelegate>(name, typeParameters);
}public static TDelegate StaticMethod<TDelegate>(this Type source, string name, params Type[] typeParameters)where TDelegate : class
{var paramsTypes = GetFuncDelegateArguments<TDelegate>();var methodInfo = GetStaticMethodInfo(source, name, paramsTypes, typeParameters);return methodInfo?.CreateDelegate(typeof(TDelegate)) as TDelegate;
}

对于具有相同数量类型参数的泛型方法,前三个只是使用1,2或3个类型参数的重载。下一个方法允许创建具有更多类型参数的委托。最重要的是执行创建委托的实际工作的最后一个方法。

要将上述方法用于泛型方法委托,我们必须按以下方式调用它们(第一个示例调用,然后是通过委托执行的目标泛型方法)。

var g1 = DelegateFactory.StaticMethod<TestClass, Func<TestClass, TestClass>, TestClass>("StaticGenericMethod");
public static T StaticGenericMethod<T>(T param)var g2 = DelegateFactory.StaticMethod<TestClass, Func<TestClass, int, TestClass>, TestClass>("StaticGenericMethod");
public static T StaticGenericMethod<T>(T param, int i) where T : ITestInterfacevar g3 = DelegateFactory.StaticMethod<TestClass, Func<TestStruct, int, bool, TestStruct>, TestStruct>("StaticGenericMethod");
public static T StaticGenericMethod<T>(T param, int i, bool p) where T : structvar g4 = DelegateFactory.StaticMethod<TestClass, Func<TestClass>, TestClass>("StaticGenericMethod");
public static T StaticGenericMethod<T>() where T : new()var g5 = DelegateFactory.StaticMethod<TestClass, Func<TestClass>, TestClass, TestStruct>("StaticGenericMethod");
public static T1 StaticGenericMethod<T1, T2>() where T1 : new()var g6 = DelegateFactory.StaticMethod<TestClass, Func<int, TestClass>, TestClass, TestStruct, int>("StaticGenericMethod");
public static T1 StaticGenericMethod<T1, T2, T3>(int i) where T1 : new()var g7 = DelegateFactory.StaticMethod<TestClass, Func<int, TestClass>>("StaticGenericMethod", typeof(TestClass), typeof(TestStruct), typeof(int));
public static T1 StaticGenericMethod<T1, T2, T3>(int i) where T1 : new()

用这种方式解释时看起来很容易 。

我们已经将StaticMethod重载作为Type实例的扩展方法。当我们想要创建带有1,2或3个类型参数的委托并且我们不知道或不想使用源类型时,我们还可以为这些情况编写三个新的重载。

public static TDelegate StaticMethod<TDelegate, TParam1>(this Type source, string name)where TDelegate : class
{return source.StaticMethod<TDelegate>(name, typeof(TParam1));
}public static TDelegate StaticMethod<TDelegate, TParam1, TParam2>(this Type source, string name)where TDelegate : class
{return source.StaticMethod<TDelegate>(name, typeof(TParam1), typeof(TParam2));
}public static TDelegate StaticMethod<TDelegate, TParam1, TParam2, TParam3>(this Type source, string name)where TDelegate : class
{return source.StaticMethod<TDelegate>(name, typeof(TParam1), typeof(TParam2), typeof(TParam3));
}

它们的使用方式与作为第一个类型参数传递的源类型相同。

好。现在,当我们不知道任何类型(或者不想使用那些类型)时,我们可以跳转到此示例。生成的委托应该使用对象类型作为参数和返回值。它是在静态方法,实例方法,属性和索引器中完成的。这个函数的主要问题是我们不能将它作为StaticMethod重载,因为之前的一个重载已经允许使用params参数。具有类型数组的任何其他重载都将是编译错误。还要记住,对于没有传递委托类型的方法,我们必须编写两个方法,对于void  返回类型和非void类型,我们将创建方法StaticGenericMethodStaticGenericMethodVoid。幸运的是,两者都可以通过与StaticMethodStaticMethodVoid 相同的代码来处理,只需进行简单的更改即可。

public static Func<object[], object> StaticGenericMethod(this Type source,string name, Type[] paramsTypes, Type[] typeParams)
{return StaticMethod<Func<object[], object>>(source, name, typeParams, paramsTypes);
}public static Action<object[]> StaticGenericMethodVoid(this Type source,string name, Type[] paramsTypes, Type[] typeParams)
{return StaticMethod<Action<object[]>>(source, name, typeParams, paramsTypes);
}

以前方法的更改实现将如下所示。

public static Func<object[], object> StaticMethod(this Type source,string name, params Type[] paramsTypes)
{return StaticMethod<Func<object[], object>>(source, name, null, paramsTypes);
}public static TDelegate StaticMethod<TDelegate>(this Type source,string name, Type[] typeParams, Type[] paramsTypes)where TDelegate : class
{var methodInfo = GetStaticMethodInfo(source, name, paramsTypes, typeParams);if (methodInfo == null){return null;}var argsArray = Expression.Parameter(typeof(object[]));var paramsExpression = new Expression[paramsTypes.Length];for (var i = 0; i < paramsTypes.Length; i++){var argType = paramsTypes[i];paramsExpression[i] =Expression.Convert(Expression.ArrayIndex(argsArray, Expression.Constant(i)), argType);}Expression returnExpression = Expression.Call(methodInfo, paramsExpression);if (methodInfo.ReturnType != typeof(void) && !methodInfo.ReturnType.IsClass){returnExpression = Expression.Convert(returnExpression, typeof(object));}return Expression.Lambda(returnExpression, argsArray).Compile() as TDelegate;
}public static Action<object[]> StaticMethodVoid(this Type source,string name, params Type[] paramsTypes)
{return StaticMethod<Action<object[]>>(source, name, null, paramsTypes);
}

现在静态泛型方法的一切都准备好了,我们可以开始测试。下面的调用将为StaticGenericMethod(每个块中的行)不同重载创建委托,并使用StaticMethodStaticGenericMethod(三个块)的不同重载。

var g1 = DelegateFactory.StaticMethod<TestClass, Func<TestClass, TestClass>, TestClass>("StaticGenericMethod");
var g2 = DelegateFactory.StaticMethod<TestClass, Func<TestClass, int, TestClass>, TestClass>("StaticGenericMethod");
var g3 = DelegateFactory.StaticMethod<TestClass, Func<TestStruct, int, bool, TestStruct>, TestStruct>("StaticGenericMethod");
var g4 = DelegateFactory.StaticMethod<TestClass, Func, TestClass>("StaticGenericMethod");
var g5 = DelegateFactory.StaticMethod<TestClass, Func, TestClass, TestStruct>("StaticGenericMethod");
var g6 = DelegateFactory.StaticMethod<TestClass, Func<int, TestClass>, TestClass, TestStruct, int>("StaticGenericMethod");
var g7 = DelegateFactory.StaticMethod<TestClass, Func<int, TestClass>>("StaticGenericMethod", typeof(TestClass), typeof(TestStruct), typeof(int));var g8 = Type.StaticMethod<Func<TestClass, TestClass>, TestClass>("StaticGenericMethod");
var g9 = Type.StaticMethod<Func<TestClass, int, TestClass>, TestClass>("StaticGenericMethod");
var g10 = Type.StaticMethod<Func<TestStruct, int, bool, TestStruct>, TestStruct>("StaticGenericMethod");
var g11 = Type.StaticMethod<Func, TestClass>("StaticGenericMethod");
var g12 = Type.StaticMethod<Func, TestClass, TestStruct>("StaticGenericMethod");
var g13 = Type.StaticMethod<Func<int, TestClass>, TestClass, TestStruct, int>("StaticGenericMethod");
var g14 = Type.StaticMethod<Func<int, TestClass>>("StaticGenericMethod", typeof(TestClass), typeof(TestStruct), typeof(int));var g15 = Type.StaticGenericMethod("StaticGenericMethod", new[] { Type }, new[] { Type });
var g16 = Type.StaticGenericMethod("StaticGenericMethod", new[] { Type, typeof(int) }, new[] { Type });
var g17 = Type.StaticGenericMethod("StaticGenericMethod", new[] { typeof(TestStruct), typeof(int), typeof(bool) }, new[] { typeof(TestStruct) });
var g18 = Type.StaticGenericMethod("StaticGenericMethod", Type.EmptyTypes, new[] { Type });
var g19 = Type.StaticGenericMethod("StaticGenericMethod", Type.EmptyTypes, new[] { Type, typeof(TestStruct) });
var g20 = Type.StaticGenericMethod("StaticGenericMethod", new[] { typeof(int) }, new[] { Type, typeof(TestStruct), typeof(int) });
var g21 = Type.StaticGenericMethodVoid("StaticGenericMethodVoid", new[] { Type }, new[] { Type });

创建的委托按以下方式使用。

var t = g1(TestInstance);
var t2 = g2(TestInstance, 0);
var t3 = g3(new TestStruct(), 0, false);
var t4 = g4();
var t5 = g5();
var t6 = g6(0);
var t7 = g7(0);var t8 = g8(TestInstance);
var t9 = g9(TestInstance, 0);
var t10 = g10(new TestStruct(), 0, false);
var t11 = g11();
var t12 = g12();
var t13 = g13(0);
var t14 = g14(0);var t15 = g15(new object[] { TestInstance });
var t16 = g16(new object[] { TestInstance, 0 });
var t17 = g17(new object[] { new TestStruct(), 0, false });
var t18 = g18(new object[] { });
var t19 = g19(new object[] { });
var t20 = g20(new object[] { 0 });
g21(new object[] { TestInstance });
var t21 = TestClass.StaticGenericMethodVoidParameter;

我们还可以针对直接调用和反射的泛型方法测试委托的性能。

_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{var test = TestClass.StaticGenericMethod(TestInstance);
}
_stopWatch.Stop();
Console.WriteLine("Static generic method directly: {0}", _stopWatch.ElapsedMilliseconds);_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{var test = g1(TestInstance);
}
_stopWatch.Stop();
Console.WriteLine("Static generic method proxy: {0}", _stopWatch.ElapsedMilliseconds);var methodInfos = Type.GetMethods(BindingFlags.Static | BindingFlags.Public).Where(m => m.Name == "StaticGenericMethod" && m.IsGenericMethod && m.GetParameters().Length == 1 && m.GetGenericArguments().Length==1);
var methodInfo = methodInfos.Single();
methodInfo = methodInfo.MakeGenericMethod(Type);_stopWatch = new Stopwatch();
_stopWatch.Start();
for (var i = 0; i < _delay; i++)
{var test = methodInfo.Invoke(null, new object[] { TestInstance });
}
_stopWatch.Stop();
Console.WriteLine("Static generic method via reflection: {0}", _stopWatch.ElapsedMilliseconds);

上面的代码将在控制台中生成输出类似的行。

Static generic method directly: 1160
Static generic method proxy: 1270
Static generic method via reflection: 21591

正如您所看到的,性能与其他委托对不同的成员类型一致。

实例泛型方法

我们现在可以对实例方法做同样的事情。但首先,我们必须以与GetStaticMethodInfo相同的方式更改GetMethodInfo

private static MethodInfo GetMethodInfo(Type source, string name, Type[] parametersTypes, Type[] typeParameters = null)
{MethodInfo methodInfo = null;try{methodInfo = (source.GetMethod(name, BindingFlags.Instance | BindingFlags.Public, null, parametersTypes, null) ??source.GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic, null, parametersTypes, null)) ??source.GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, parametersTypes, null);}catch (AmbiguousMatchException){//swallow and test generics}//check for generic methodsif (typeParameters != null){var ms = source.GetMethods(BindingFlags.Instance | BindingFlags.Public).Concat(source.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)).Concat(source.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));foreach (var m in ms){if (m.Name == name && m.IsGenericMethod){var parameters = m.GetParameters();var genericArguments = m.GetGenericArguments();var parametersTypesValid = parameters.Length == parametersTypes.Length;parametersTypesValid &= genericArguments.Length == typeParameters.Length;if (!parametersTypesValid){continue;}for (var index = 0; index < parameters.Length; index++){var parameterInfo = parameters[index];var parameterType = parametersTypes[index];if (parameterInfo.ParameterType != parameterType&& parameterInfo.ParameterType.IsGenericParameter&& !parameterInfo.ParameterType.CanBeAssignedFrom(parameterType)){parametersTypesValid = false;break;}}for (var index = 0; index < genericArguments.Length; index++){var genericArgument = genericArguments[index];var typeParameter = typeParameters[index];if (!genericArgument.CanBeAssignedFrom(typeParameter)){parametersTypesValid = false;break;}}if (parametersTypesValid){methodInfo = m.MakeGenericMethod(typeParameters);break;}}}}return methodInfo;
}

正如你所看到的,代码几乎是一样的。区别在于绑定标志——传递实例而不是静态

现在我们可以添加新的重载。我们应该只以使用类型参数的重载开始,就像使用静态方法一样。它们看起来像这样。

public static TDelegate InstanceMethod<TDelegate, TParam1>(string name)where TDelegate : class
{return InstanceMethod<TDelegate>(name, typeof(TParam1));
}public static TDelegate InstanceMethod<TDelegate, TParam1, TParam2>(string name)where TDelegate : class
{return InstanceMethod<TDelegate>(name, typeof(TParam1), typeof(TParam2));
}public static TDelegate InstanceMethod<TDelegate, TParam1, TParam2, TParam3>(string name)where TDelegate : class
{return InstanceMethod<TDelegate>(name, typeof(TParam1), typeof(TParam2), typeof(TParam3));
}public static TDelegate InstanceMethod<TDelegate>(string name, params Type[] typeParameters)where TDelegate : class
{var paramsTypes = GetFuncDelegateArguments<TDelegate>();var source = paramsTypes.First();paramsTypes = paramsTypes.Skip(1).ToArray();var methodInfo = GetMethodInfo(source, name, paramsTypes, typeParameters);return methodInfo?.CreateDelegate(typeof(TDelegate)) as TDelegate;
}

代码非常类似于不支持泛型的代码,新的重载也很容易理解。

同样的情况是扩展方法重载。唯一的区别是传递给GetMethodInfo方法的额外参数和类型参数。

public static TDelegate InstanceMethod<TDelegate, TParam1>(this Type source, string name)where TDelegate : class
{return source.InstanceMethod<TDelegate>(name, new[] { typeof(TParam1) });
}public static TDelegate InstanceMethod<TDelegate, TParam1, TParam2>(this Type source, string name)where TDelegate : class
{return source.InstanceMethod<TDelegate>(name, new[] { typeof(TParam1), typeof(TParam2) });
}public static TDelegate InstanceMethod<TDelegate, TParam1, TParam2, TParam3>(this Type source, string name)where TDelegate : class
{return source.InstanceMethod<TDelegate>(name, new[] { typeof(TParam1), typeof(TParam2), typeof(TParam3) });
}public static TDelegate InstanceMethod<TDelegate>(this Type source, string name, Type[] typeParams = null)where TDelegate : class
{var delegateParams = GetFuncDelegateArguments<TDelegate>();var instanceParam = delegateParams[0];delegateParams = delegateParams.Skip(1).ToArray();var methodInfo = GetMethodInfo(source, name, delegateParams, typeParams);if (methodInfo == null){return null;}Delegate deleg;if (instanceParam == source){deleg = methodInfo.CreateDelegate(typeof(TDelegate));}else{var sourceParameter = Expression.Parameter(typeof(object));var expressions = delegateParams.Select(Expression.Parameter).ToArray();Expression returnExpression = Expression.Call(Expression.Convert(sourceParameter, source),methodInfo, expressions.Cast<Expression>());if (methodInfo.ReturnType != typeof(void) && !methodInfo.ReturnType.IsClass){returnExpression = Expression.Convert(returnExpression, typeof(object));}var lamdaParams = new[] { sourceParameter }.Concat(expressions);deleg = Expression.Lambda(returnExpression, lamdaParams).Compile();}return deleg as TDelegate;
}

最后两个更通用的方法,只接受对象参数和返回对象,也非常相似,并以与静态泛型相同的方式更改。

public static Func<object, object[], object> InstanceGenericMethod(this Type source,string name, Type[] paramsTypes, Type[] typeParams)
{return InstanceGenericMethod<Func<object, object[], object>>(source, name, typeParams, paramsTypes);
}public static Action<object, object[]> InstanceGenericMethodVoid(this Type source,string name, Type[] paramsTypes, Type[] typeParams)
{return InstanceGenericMethod<Action<object, object[]>>(source, name, typeParams, paramsTypes);
}public static Func<object, object[], object> InstanceMethod(this Type source,string name, params Type[] paramsTypes)
{return InstanceGenericMethod<Func<object, object[], object>>(source, name, null, paramsTypes);
}public static Action<object, object[]> InstanceMethodVoid(this Type source,string name, params Type[] paramsTypes)
{return InstanceGenericMethod<Action<object, object[]>>(source, name, null, paramsTypes);
}public static TDelegate InstanceGenericMethod<TDelegate>(this Type source,string name, Type[] typeParams, Type[] paramsTypes)where TDelegate : class
{var methodInfo = GetMethodInfo(source, name, paramsTypes, typeParams);if (methodInfo == null){return null;}var argsArray = Expression.Parameter(typeof(object[]));var sourceParameter = Expression.Parameter(typeof(object));var paramsExpression = new Expression[paramsTypes.Length];for (var i = 0; i < paramsTypes.Length; i++){var argType = paramsTypes[i];paramsExpression[i] =Expression.Convert(Expression.ArrayIndex(argsArray, Expression.Constant(i)), argType);}Expression returnExpression = Expression.Call(Expression.Convert(sourceParameter, source),methodInfo, paramsExpression);if (methodInfo.ReturnType != typeof(void) && !methodInfo.ReturnType.IsClass){returnExpression = Expression.Convert(returnExpression, typeof(object));}return Expression.Lambda(returnExpression, sourceParameter, argsArray).Compile() as TDelegate;
}

这就是我们需要支持实例泛型方法的全部内容。现在我们可以编写一些测试方法。

public T GenericMethod<T>(T s)
{return s;
}public object InstanceGenericMethodVoidParameter;public void GenericMethodVoid<T>(T s)
{InstanceGenericMethodVoidParameter = s;
}

要为上面创建委托,我们以与静态等价物相同的方式调用新创建的重载。唯一的区别是委托的一个额外参数——实例。

var ig1 = DelegateFactory.InstanceMethod<Func<TestClass, TestClass, TestClass>, TestClass>("GenericMethod");
var ig2 = Type.InstanceMethod<Func<TestClass, TestClass, TestClass>, TestClass>("GenericMethod");
var ig3 = Type.InstanceMethod<Func<object, TestClass, TestClass>, TestClass>("GenericMethod");
var ig4 = Type.InstanceGenericMethod("GenericMethod", new[] { Type }, new[] { Type });
var ig5 = Type.InstanceGenericMethodVoid("GenericMethodVoid", new[] { Type }, new[] { Type });

委托的调用方式与任何其他实例方法委托的方式相同。

var it1 = ig1(TestInstance, TestInstance);
var it2 = ig2(TestInstance, TestInstance);
var it3 = ig3(TestInstance, TestInstance);
var it4 = ig4(TestInstance, new object[] { TestInstance });
ig5(TestInstance, new object[] { TestInstance });
var it5 = TestInstance.InstanceGenericMethodVoidParameter;

这就是我们完全支持泛型方法所需的全部内容。

正如您所看到的,泛型委托的主要区别和缺点是,如果我们需要完整类型信息,我们需要为我们需要的每个类型集合创建单独的委托。或者,可以使用更通用的类型为泛型方法创建委托。例如,如果泛型方法允许某些类型参数仅为IDisposable接口,则可以使用此接口搜索方法并使用此方法创建委托。下面的代码在编译时和运行时完全有效。

var g22 = Type.StaticGenericMethodVoid("StaticGenericMethodVoid", new[] { typeof(object) }, new[] { typeof(object) });
g22(new object[] { "" });
var t22 = TestClass.StaticGenericMethodVoidParameter;
g22(new object[] { TestInstance });
var t23 = TestClass.StaticGenericMethodVoidParameter;

现在我们讨论了很多方法。现在是时候了解最后的成员类型了——事件。

事件

与属性访问器一样,对于事件,我们还有两个访问器——添加和删除。对于两者,我们将添加方法,几乎​​没有重载。我们应该首先创建一些测试事件。

public event EventHandler PublicEvent;private event EventHandler InternalEventBackend;internal event EventHandler InternalEvent
{add { InternalEventBackend += value; }remove { InternalEventBackend -= value; }
}protected event EventHandler ProtectedEvent;private event EventHandler PrivateEvent;public void InvokeInternalEvent()
{InternalEventBackend?.Invoke(this, new InternalEventArgs());
}public void InvokePrivateEvent()
{PrivateEvent?.Invoke(this, new PrivateEventArgs());
}public void InvokeProtectedEvent()
{ProtectedEvent?.Invoke(this, new ProtectedEventArgs());
}public void InvokePublicEvent()
{PublicEvent?.Invoke(this, new PublicEventArgs());
}

四个具有不同可见性的事件。内部事件具有自定义访问器,仅用于测试此类事件是否也适用于委托。添加用于调用事件的公共方法仅用于纯测试(我们需要一种从TestClass类型之外的测试代码中引发事件的方法)。

由于要引发事件我们需要首先添加处理程序,我们将首先讨论添加访问器。

事件添加访问器

DelegateFactory中的新方法将被称为EventAdd。但首先我们需要从反射中获取EventInfo

private static EventInfo GetEventInfo(string eventName, Type sourceType)
{return (sourceType.GetEvent(eventName)?? sourceType.GetEvent(eventName, BindingFlags.NonPublic))?? sourceType.GetEvent(eventName,BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
}

这里没什么好看的。它与任何其他成员的代码相同。它只是寻找事件而不是属性或方法。

如果我们知道要访问的事件的对象类型,知道委托的类型并知道它的事件参数类型,事情就非常简单。我们只需要传递有效的委托类型,以便从访问器方法创建委托。

public static Action<TSource, EventHandler<TEventArgs>> EventAdd<TSource, TEventArgs>(string eventName)
{var sourceType = typeof(TSource);var eventInfo = GetEventInfo(eventName, sourceType);return (Action<TSource, EventHandler<TEventArgs>>)eventInfo?.AddMethod.CreateDelegate(typeof(Action<TSource, EventHandler<TEventArgs>>));
}

它的作用与为get属性访问器创建委托相同。例如,对于TestClass.PublicEvent事件,它将创建委托,可以由下面的lambda表示。

Action<TestClass, EventHandler<TestClass.PublicEventArgs>> d = (i, h) => i.PublicEvent += h;

现在考虑具有未知源类型的情况,或者当这种类型无关紧要时。在所有事件处理程序总是具有object类型的第一个参数之后,从用户的角度来看它实际上是不必要的。在这种情况下唯一的问题是必须将实例参数从对象转换为具有事件的正确类型。和以前一样,我们需要表达式。

public static Action<object, EventHandler<TEventArgs>> EventAdd<TEventArgs>(this Type source, string eventName)
{var eventInfo = GetEventInfo(eventName, source);if (eventInfo != null){var instanceParameter = Expression.Parameter(typeof(object));var delegateTypeParameter = Expression.Parameter(typeof(EventHandler<TEventArgs>));var lambda = Expression.Lambda(Expression.Call(Expression.Convert(instanceParameter, source),eventInfo.AddMethod, delegateTypeParameter),instanceParameter, delegateTypeParameter);return (Action<object, EventHandler<TEventArgs>>)lambda.Compile();}return null;
}

创建的lambda表达式只是在转换为源类型实例参数时使用第二个参数的处理程序调用事件信息AddMethod。通常,这更容易以lambda的形式理解。

Action<object, EventHandler<TestClass.PublicEventArgs>> d = (i, h) => ((TestClass)i).PublicEvent += h;

当我们知道类的类型而我们不知道事件参数的类型时,即当事件是私有的时?一个案例怎么样?创建委托的唯一方法是通过表达式(当我们不知道方法的参数时,情况类似)。不幸的是,这有点复杂,因为每个事件处理程序都接受对象和一些派生自EventArgs类型(或者因为它更像是一个约定而不是强制的)。为什么?考虑以下代码将事件绑定到处理程序。

TestInstance.PublicEvent += (sender, args) => { };

什么都不复杂吧?此代码只是将lambda函数添加到事件中。但到底怎么样?查看与上述相同的代码。

EventHandler<TestClass.PublicEventArgs> ha = (sender, args) => { };
TestInstance.PublicEvent += ha.Invoke;

使用我们的lambda函数作为目标创建EventHandler<>类型的第一个新对象。之后,Invoke方法中的相同lambda 作为处理程序传递给事件。显然我们需要从Action <objectEventArgs>类型创建EventHandler类型的对象。怎么做?最明显的尝试是使用EventHandler <>类型的构造函数。

EventHandler<TestClass.PublicEventArgs> ha = new EventHandler<TestClass.PublicEventArgs>((sender, args) => { });

这完美无瑕。由于我们已经看到如何使用反射和表达式中的构造函数,因此应该很容易创建调用从反射中检索的构造函数的表达式。嗯......不太好。

正如您所看到的,此类型只有一个构造函数,它涉及指向某个对象的指针。哪一个?当你创建EventHandler类型的新对象时,真正的发生了什么?我们可以检查从C#编译的CIL代码。

IL_0011:  ldsfld      class Delegates.TestEventHandler/'<>c' Delegates.TestEventHandler/'<>c'::'<>9'
IL_0016:  ldftn       instance void Delegates.TestEventHandler/'<>c'::'<.ctor>b__0_0'(object, class Delegates.TestClass/PublicEventArgs)
IL_001c:  newobj    instance void class [mscorlib]System.EventHandler`1<class Delegates.TestClass/PublicEventArgs>::.ctor(object, native int)

神奇的事情发生在ldftn指令中,这意味着:'将指针推送到堆栈上方法引用的方法’。根据这个页面。这意味着加载了指向方法senderargs=> {}指针,并使用此指针创建新的EventHandler。因此我们无法使用此构造函数,因为我们无法从C#获取指向托管方法的指针。

那我们能做什么呢?最简单的解决方案是创建为我们执行此操作的新方法。简单调用新的EventHandler()就足够了。然后我们可以在表达式中使用该方法。

再次出现一个小问题。如您所知,如果您没有此处理程序实例,则无法删除事件处理程序。而且您无法创建事件实例,因为您不知道它的类型。我们唯一能做的就是保存用户处理程序(即Action<objec, object>)和真正的EventHandler <EventArgs>处理程序之间的某种关系。当然可以编写EventAdd方法,其创建委托,返回事件处理程序实例。但对于希望以与事件相同的方式(或至少尽可能接近)与委托一起工作的最终用户来说,这将是某种不直观的方式。因为最好的想法是在DelegateFactory中将关系存储在字典中——它对用户来说是透明的。

private static readonly Dictionary<WeakReference<object>, WeakReference<object>> EventsProxies =new Dictionary<WeakReference<object>, WeakReference<object>>();

现在我们可以实现EventHandlerFactory方法。

public static EventHandler<TEventArgs> EventHandlerFactory<TEventArgs, TSource>(object handler, bool isRemove)where TEventArgs : class
{EventHandler<TEventArgs> newEventHandler;var haveKey = false;var kv = EventsProxies.FirstOrDefault(k =>{object keyTarget;k.Key.TryGetTarget(out keyTarget);if (Equals(keyTarget, handler)){haveKey = true;return true;}return false;});if (haveKey){object fromCache;EventsProxies[kv.Key].TryGetTarget(out fromCache);newEventHandler = (EventHandler<TEventArgs>)fromCache;if (isRemove){EventsProxies.Remove(kv.Key);return newEventHandler;}}if (!isRemove){var action = handler as Action<TSource, object>;if (action != null){newEventHandler = (s, a) => action((TSource)s, a);}else{newEventHandler = new EventHandler<TEventArgs>((Action<object, object>)handler);}EventsProxies[new WeakReference<object>(handler)] = new WeakReference<object>(newEventHandler);return newEventHandler;}return null;
}

想法很简单。首先,我们在EventProxies字典中寻找已经生成的委托。如果存储在WeakReference中的引用键等于提供的处理程序,我们存储键值对结构并设置hasKey变量。我们必须使用单独的bool变量,因为KeyValuePair类型是结构,我们不能对结构执行空检查。如果找到键,我们从WeakReference目标中检索事件处理程序。isRemove参数用于区分为添加访问器和删除访问器检索/创建事件处理程序。为了删除事件处理程序,我们不需要先创建它(因为我们无法以这种方式执行处理程序),如果我们检索已保存的处理程序,我们可以同时从缓存中删除它。对于添加访问器,如果在缓存中找不到事件处理程序,我们可以创建新的访问者。如果提供的动作处理程序具有类型不同于对象的第一个参数,我们需要另一个lambda代理和对象参数。 这是因为.NET中的事件始终具有对象的第一个参数,并且EventHandler不接受不同的签名。否则,我们可以安全地直接使用动作,将其传递给EventHandler构造函数。这就是处理程序参数具有对象类型——我们需要一种方法来传递方法的不同签名,具体取决于EventAdd / EventRemove的重载。保存新创建的事件处理程序后,我们就完成了。

由于我们正在使用事件处理程序的不兼容签名,并且我们正在编写这样复杂的工厂方法以便创建正确的工具方法,我们还需要一个技巧来从表达式中使用它(并且没有其他方法)。DelegateFactory类中的新字段是足够的。

private static readonly MethodInfo EventHandlerFactoryMethodInfo = typeof(DelegateFactory).GetMethod("EventHandlerFactory");

此字段将以与任何其他MethodInfo对象类似的方式在表达式内使用,但在提供类型参数之后(因为它是泛型方法)。

现在我们可以使用提供的处理程序编写具有源类型的第一个参数的EventAdd新实现。

public static Action<TSource, Action<TSource, object>> EventAdd<TSource>(string eventName)
{var source = typeof(TSource);var eventInfo = GetEventInfo(eventName, source);if (eventInfo != null){var eventArgsType = eventInfo.EventHandlerType.GetGenericArguments()[0];var delegateType = typeof(Action<,>).MakeGenericType(typeof(TSource), typeof(object));var instanceParameter = Expression.Parameter(source);var delegateTypeParameter = Expression.Parameter(delegateType);var methodCallExpression =Expression.Call(EventHandlerFactoryMethodInfo.MakeGenericMethod(eventArgsType, source), delegateTypeParameter, Expression.Constant(false));var lambda = Expression.Lambda(Expression.Call(instanceParameter, eventInfo.AddMethod, methodCallExpression),instanceParameter, delegateTypeParameter);return lambda.Compile() as Action<TSource, Action<TSource, object>>;}return null;
}

这个重载与前一个重载非常类似,除了直接传递委托给添加访问器而不是传递EventHandlerFactory方法的结果。为此,我们需要从eventInfo.EventHandlerType获取事件参数类型。对于类型,我们都创建非泛型方法,并使用不兼容的处理程序作为参数并使用Boolean.False常量来调用它。这样我们将从缓存或EventHandler构造函数中获取正确的处理程序。当然,delegateTypeParameter是对象类型(因为这是EventHandlerFactory方法中第一个参数的类型)而不是EventHandler <>。结果委托将类似于下面的lambda。

Action<TestClass, object> d = (i, h) => i.PublicEvent += DelegateFactory.EventHandlerFactory<TestClass.PublicEventArgs,TestClass>(h, false);

现在我们可以实现EventAdd方法的最后一个重载并且更加可用,因为不需要知道任何类型(源类型或事件参数类型)。其实现如下。

public static Action<object, Action<object, object>> EventAdd(this Type source, string eventName)
{var eventInfo = GetEventInfo(eventName, source);if (eventInfo != null){var eventArgsType = eventInfo.EventHandlerType.GetGenericArguments()[0];var instanceParameter = Expression.Parameter(typeof(object));var delegateTypeParameter = Expression.Parameter(typeof(object));var methodCallExpression =Expression.Call(EventHandlerFactoryMethodInfo.MakeGenericMethod(eventArgsType, source), delegateTypeParameter, Expression.Constant(false));var lambda = Expression.Lambda(Expression.Call(Expression.Convert(instanceParameter, source),eventInfo.AddMethod, methodCallExpression),instanceParameter, delegateTypeParameter);return lambda.Compile() as Action<object, Action<object, object>>;}return null;
}

与以前的代码相同。唯一的区别是产生的委托类型。这是因为我们编写了EventHandlerFactory来支持两者。因此,我们可以使用实现创建第三种方法并重写前两种方法。

private static TDelegate EventAddImpl<TDelegate>(this Type source, string eventName)where TDelegate : class
{var eventInfo = GetEventInfo(eventName, source);if (eventInfo != null){var eventArgsType = eventInfo.EventHandlerType.GetGenericArguments()[0];var instanceParameter = Expression.Parameter(typeof(object));var delegateTypeParameter = Expression.Parameter(typeof(object));var methodCallExpression =Expression.Call(EventHandlerFactoryMethodInfo.MakeGenericMethod(eventArgsType, source), delegateTypeParameter, Expression.Constant(false));var lambda = Expression.Lambda(Expression.Call(Expression.Convert(instanceParameter, source),eventInfo.AddMethod, methodCallExpression),instanceParameter, delegateTypeParameter);return lambda.Compile() as TDelegate;}return null;
}public static Action<TSource, Action<TSource, object>> EventAdd<TSource>(string eventName)
{var source = typeof(TSource);return source.EventAddImpl<Action<TSource, Action<TSource, object>>>(eventName);
}public static Action<object, Action<object, object>> EventAdd(this Type source, string eventName)
{return source.EventAddImpl<Action<object, Action<object, object>>>(eventName);
}

这是我们在不同情况下为添加访问器创建委托所需的。现在我们可以测试它们,以确保一切正常。应按以下方式调用EventAdd重载。

var ea1 = DelegateFactory.EventAdd<TestClass, TestClass.PublicEventArgs>("PublicEvent");
var ea2 = DelegateFactory.EventAdd<TestClass, TestClass.InternalEventArgs>("InternalEvent");
var ea3 = DelegateFactory.EventAdd<TestClass>("ProtectedEvent");
var ea4 = Type.EventAdd<TestClass.PublicEventArgs>("PublicEvent");
var ea5 = Type.EventAdd("PrivateEvent");

要测试此委托是否有效,我们需要创建一些处理程序。

private static void HandlerWithoutSourceType(object o, TestClass.PublicEventArgs eventArgs)
{Console.WriteLine("Public handler without source type works!");
}private static void HandlerWithSourceType(TestClass sender, object eventArgs)
{if (eventArgs.GetType() ==Type.GetNestedType("ProtectedEventArgs", BindingFlags.Instance | BindingFlags.NonPublic)){Console.WriteLine("Protected handler works!");}
}private static void TypelessHandler(object sender, object eventArgs)
{if (eventArgs is TestClass.PublicEventArgs){Console.WriteLine("Public handler works!");}else if (eventArgs is TestClass.InternalEventArgs){Console.WriteLine("Internal handler works!");}else if (eventArgs.GetType() ==Type.GetNestedType("PrivateEventArgs", BindingFlags.Instance | BindingFlags.NonPublic)){Console.WriteLine("Private handler works!");}
}

现在我们可以使用委托将处理程序绑定到事件。

ea1(TestInstance, TypelessHandler);
ea2(TestInstance, TypelessHandler);
ea3(TestInstance, HandlerWithSourceType);
ea4(TestInstance, HandlerWithoutSourceType);
ea5(TestInstance, TypelessHandler);

这就是处理程序方法在控制台中写入不同文本集的原因——并非每个人都处理所有事件。

TestInstance.InvokePublicEvent();
TestInstance.InvokeInternalEvent();
TestInstance.InvokeProtectedEvent();
TestInstance.InvokePrivateEvent();

在通过上面的特殊公共方法调用事件之后,我们将在控制台中看到如下输出。

Public handler works!
Public handler without source type works!
Internal handler works!
Protected handler works!
Private handler works!

这意味着每个事件处理程序都可以工作,尽管可见性不同,用户处理程序,自定义和默认访问器的签名也不同。

事件删除访问者

删除访问器最好的一点是,有与添加访问器相同的签名。在EventHandlerFactory方法中处理删除访问器的主要问题,这需要完全相同的引用。在EventRemove方法中,我们只需要改变eventInfo.AddMethodeventInfo.RemoveMethod并传递Boolean.True而不是Boolean.FalseEventHandlerFacory方法(从词典中删除事件处理程序的不必要的实例)中。

public static Action<TSource, EventHandler<TEventArgs>> EventRemove<TSource, TEventArgs>(string eventName)
{var source = typeof(TSource);var eventInfo = GetEventInfo(eventName, source);return (Action<TSource, EventHandler<TEvent>>)eventInfo?.RemoveMethod.CreateDelegate(typeof(Action<TSource, EventHandler<TEvent>>));
}public static Action<object, EventHandler<TEventArgs>> EventRemove<TEventArgs>(this Type source, string eventName)
{var eventInfo = GetEventInfo(eventName, source);if (eventInfo != null){var instanceParameter = Expression.Parameter(typeof(object));var delegateTypeParameter = Expression.Parameter(typeof(EventHandler<TEventArgs>));var lambda = Expression.Lambda(Expression.Call(Expression.Convert(instanceParameter, source),eventInfo.RemoveMethod, delegateTypeParameter),instanceParameter, delegateTypeParameter);return (Action<object, EventHandler<TEventArgs>>)lambda.Compile();}return null;
}public static Action<TSource, Action<TSource, object>> EventRemove<TSource>(string eventName)
{return typeof(TSource).EventRemoveImpl<Action<TSource, Action<TSource, object>>>(eventName);
}public static Action<object, Action<object, object>> EventRemove(this Type source, string eventName)
{return source.EventRemoveImpl<Action<object, Action<object, object>>>(eventName);
}private static TDelegate EventRemoveImpl<TDelegate>(this Type source, string eventName)where TDelegate : class
{var eventInfo = GetEventInfo(eventName, source);if (eventInfo != null){var eventArgsType = eventInfo.EventHandlerType.GetGenericArguments()[0];var instanceParameter = Expression.Parameter(typeof(object));var delegateTypeParameter = Expression.Parameter(typeof(object));var methodCallExpression =Expression.Call(EventHandlerFactoryMethodInfo.MakeGenericMethod(eventArgsType, source), delegateTypeParameter, Expression.Constant(true));var lambda = Expression.Lambda(Expression.Call(Expression.Convert(instanceParameter, source),eventInfo.RemoveMethod, methodCallExpression),instanceParameter, delegateTypeParameter);return lambda.Compile() as TDelegate;}return null;
}

代码几乎相同。因此,我们可以重写所有这些方法来为选定的访问者构建委托。我们只需要两个常量和额外的代码来选择正确的访问器MethodInfo

private const string AddAccessor = "add";
private const string RemoveAccessor = "remove";public static Action<object, EventHandler<TEventArgs>> EventRemove<TEventArgs>(this Type source, string eventName)
{return EventAccessor<TEventArgs>(source, eventName, RemoveAccessor);
}public static Action<TSource, EventHandler<TEventArgs>> EventRemove<TSource, TEventArgs>(string eventName)
{return EventAccessor<TSource, TEventArgs>(eventName, RemoveAccessor);
}public static Action<TSource, Action<TSource, object>> EventRemove<TSource>(string eventName)
{return typeof(TSource).EventRemoveImpl<Action<TSource, Action<TSource, object>>>(eventName);
}public static Action<object, Action<object, object>> EventRemove(this Type source, string eventName)
{return source.EventRemoveImpl<Action<object, Action<object, object>>>(eventName);
}

具有逻辑的方法将包含先前讨论的所有代码。

private static Action<object, EventHandler<TEventArgs>> EventAccessor<TEventArgs>(Type source, string eventName, string accessorName)
{var accessor = GetEventInfoAccessor(eventName, source, accessorName);if (accessor != null){var instanceParameter = Expression.Parameter(typeof(object));var delegateTypeParameter = Expression.Parameter(typeof(EventHandler<TEventArgs>));var lambda = Expression.Lambda(Expression.Call(Expression.Convert(instanceParameter, source),accessor, delegateTypeParameter),instanceParameter, delegateTypeParameter);return (Action<object, EventHandler<TEventArgs>>)lambda.Compile();}return null;
}private static Action<TSource, EventHandler<TEventArgs>> EventAccessor<TSource, TEventArgs>(string eventName, string accessorName)
{var sourceType = typeof(TSource);var accessor = GetEventInfoAccessor(eventName, sourceType, accessorName);return (Action<TSource, EventHandler<TEventArgs>>)accessor?.CreateDelegate(typeof(Action<TSource, EventHandler<TEventArgs>>));
}private static TDelegate EventAccessorImpl<TDelegate>(Type source, string eventName, string accessorName)where TDelegate : class
{var eventInfo = GetEventInfo(eventName, source);if (eventInfo != null){var accessor = accessorName == AddAccessor ? eventInfo.AddMethod : eventInfo.RemoveMethod;var eventArgsType = eventInfo.EventHandlerType.GetGenericArguments()[0];var instanceParameter = Expression.Parameter(typeof(object));var delegateTypeParameter = Expression.Parameter(typeof(object));var methodCallExpression =Expression.Call(EventHandlerFactoryMethodInfo.MakeGenericMethod(eventArgsType, source),delegateTypeParameter, Expression.Constant(accessorName == RemoveAccessor));var lambda = Expression.Lambda(Expression.Call(Expression.Convert(instanceParameter, source),accessor, methodCallExpression),instanceParameter, delegateTypeParameter);return lambda.Compile() as TDelegate;}return null;
}private static TDelegate EventRemoveImpl<TDelegate>(this Type source, string eventName)where TDelegate : class
{return EventAccessorImpl<TDelegate>(source, eventName, RemoveAccessor);
}

好。最大的区别是获得访问者。新方法如下所示。

private static MethodInfo GetEventInfoAccessor(string eventName, Type sourceType, string accessor)
{var eventInfo = GetEventInfo(eventName, sourceType);return accessor == AddAccessor ? eventInfo?.AddMethod : eventInfo?.RemoveMethod;
}

除了在EventAccessorImpl方法中获取访问器之外,值得一提的是为EventHandlerFactory设置标志的代码

要测试委托是否有效,我们可以使用它们然后触发事件——如果删除事件有效,我们将无法在控制台中看到任何内容。为了获得委托,我们可以执行以下行。

var er1 = DelegateFactory.EventRemove<TestClass, TestClass.PublicEventArgs>("PublicEvent");
var er2 = DelegateFactory.EventRemove<TestClass, TestClass.InternalEventArgs>("InternalEvent");
var er3 = DelegateFactory.EventRemove<TestClass>("ProtectedEvent");
var er4 = Type.EventRemove<TestClass.PublicEventArgs>("PublicEvent");
var er5 = Type.EventRemove("PrivateEvent");

在之前的委托之后调用新创建的委托(对于添加访问者)应该删除处理程序。

er1(TestInstance, TypelessHandler);
er2(TestInstance, TypelessHandler);
er3(TestInstance, HandlerWithSourceType);
er4(TestInstance, HandlerWithoutSourceType);
er5(TestInstance, TypelessHandler);TestInstance.InvokePublicEvent();
TestInstance.InvokeInternalEvent();
TestInstance.InvokeProtectedEvent();
TestInstance.InvokePrivateEvent();

在添加访问器的测试代码之后立即调用上面的行将导致仅在控制台中输出测试文本一次。这意味着它有效。一切都正常运行。

总结

在本文中,我们介绍了如何以及为什么为泛型方法(静态和实例)和事件创建委托。与前两篇文章一起,我们涵盖了所有成员,现在可以快速访问所有成员,而无需关注反射的可见性或性能。

可以在github上找到本文和以前的代码。

上一篇 比反射更快:委托 第2部分

原文地址:https://www.codeproject.com/Articles/1124863/Faster-than-Reflection-Delegates-Part-2

比反射更快:委托 第3部分相关推荐

  1. 比反射更快:委托 第2部分

    目录 构造函数 静态方法 实例方法 总结 GitHub和Nuget包中提供了具有新功能和错误修复的代码. 现在是时候介绍以下成员了.(如想了解字段.属性等情况,可查看上一篇文章) 静态的 方法 例 方 ...

  2. 比反射更快:委托 第1部分

    目录 为什么不用反射? 委托一切 静态属性 获取访问器(get accessors) 设置访问器(set accessors) 改进 属性 改进 索引器 改进 Setters 静态字段 获取静态字段值 ...

  3. 究竟是什么可以比反射还快实现动态调用?

    戏精分享 C#表达式树,第一季正式完稿 前不久,我们发布了<只要十步,你就可以应用表达式树来优化动态调用>. 观众们普遍反映文章的内容太多复杂不太容易理解. 因此,我们以此为契机发布了&l ...

  4. 究竟是什么可以比反射还快实现动态调用?| Source Generators版

    前言 最近在公众号上看到一篇文章<究竟是什么可以比反射还快实现动态调用?>,它使用的是Newbe.ObjectVisitor,基于C#表达式树访问一个普通class的所有属性和对应的值,可 ...

  5. 如何更快地渲染?深入了解3D渲染性能的指南!(5)

    上文<如何更快地渲染?深入了解3D渲染性能的指南!(4)>我们介绍了从场景内部灯光.用料.反射.材质等方面的优化,提升场景渲染的速度.本文Renderbus云渲染农场将继续从渲染设置.全局 ...

  6. 哪个更快?document.addEventListener VS element.addEventListener

    问题描述: 在创建网页的时候,我有一个困惑,我注意到有很多元素等待点击,所以我想问:哪种做法对性能更好? 为文档添加事件侦听器,然后检查我单击的元素的类或 id: document.addEventL ...

  7. 更快更稳更优质:华为云CDN下载加速解决方案测评

    目录 一.前言 二.下载业务面临的三大难题 1.业务下载缓慢 2.业务稳定性受挑战 3.高昂的运维成本 三.完美解决方案:CDN下载加速 四.为什么选择华为云? 1.快速高效,极致体验 2.稳定可靠, ...

  8. 如何更快地渲染?深入了解3D渲染性能的指南!(2)

    如何更快地渲染-概述: 我将把本文分为两个主要部分: 可以优化的内部渲染因子 可以优化的外部渲染因子 您会发现内部渲染因子与您想要更快地优化/渲染的场景紧密相关,需要更改场景元素和渲染设置. 另一方面 ...

  9. 如何更快地渲染?深入了解3D渲染性能的指南!(4)

    上文<如何更快地渲染?深入了解3D渲染性能的指南!(3)>中,我们介绍了如何快速渲染中对场景中多边形数量的优化,从而加速场景的渲染速度.本文Renderbus云渲染农场将继续带您从场景内部 ...

最新文章

  1. Android--获取高清的app图标
  2. 通用扩展函数--类型转换
  3. Linux jdk配置/下载/安装简单说明
  4. java try finally connectoin close_Java I/O流详解
  5. yum因被锁定导致无法执行相关操作的解决方法
  6. js中避免函数名和变量名跟别人冲突
  7. [GO]无缓冲通道(unbuffered channel)
  8. redis的主从复制和高可用集群
  9. 用ubuntu制作ubuntu系统启动盘
  10. Linux移植libmodbus
  11. excel两列数据对比找不同_怎么在excel中对比两列数据并查找重复项?
  12. 7大Python IDE工具推荐
  13. Jenkins 登录时离线 offline
  14. 一种简单的业务数据监控告警设计方案
  15. Arduino ESP32Web配网
  16. [个人笔记]HCIP-Routing Switching-IEEP/H12-223
  17. Word中常见的论文三线表(表格)制作
  18. 深度linux安装cad,在deepin下安装CAD — 原生CAD 看图画图
  19. Unity通过Animator获取动画clip时长
  20. Spring定时任务多线程

热门文章

  1. 用Linux同时编辑两个文档,如何使用Vim编辑多个文件
  2. php替换指定函数,PHP替换指定字符函数str_replace()的用法
  3. 疫情海报模板|光效显微传播大数据必备psd素材
  4. oracle关联表查询使用索引_SQL技巧:查询某个表关联的所有存储过程
  5. qwt需要添加到qcreator的东西
  6. Linux开机启动过程(3):显示模式初始化和进入保护模式
  7. 分布式事务的特征、原理、以及常见3种解决方案
  8. srsLTE源码学习:安全证书polarssl
  9. shell之xargs与-exec与管道的区别你造吗?
  10. Django:ORM模型类,字段选项,Meta内部类,常见问题处理