/// <summary> /// Creates a delegate of a specified type which wraps another similar delegate, doing downcasts where necessary. /// The created delegate will only work in case the casts are valid. /// </summary> /// <typeparam name = "TDelegate">The type for the delegate to create.</typeparam> /// <param name = "toWrap">The delegate which needs to be wrapped by another delegate.</param> /// <returns>A new delegate which wraps the passed delegate, doing downcasts where necessary.</returns> public static TDelegate WrapDelegate <TDelegate>(Delegate toWrap) where TDelegate : class { if (!typeof(TDelegate).GetTypeInfo().IsSubclassOf(typeof(MulticastDelegate))) { throw new ArgumentException("Specified type should be a delegate."); } MethodInfo toCreateInfo = MethodInfoFromDelegateType(typeof(TDelegate)); MethodInfo toWrapInfo = toWrap.GetMethodInfo(); // Create delegate original and converted parameters. // TODO: In the unlikely event that someone would create a delegate with a Closure argument, the following logic will fail. Add precondition to check this? IEnumerable <Type> toCreateArguments = toCreateInfo.GetParameters().Select(d => d.ParameterType); ParameterInfo[] test = toWrapInfo.GetParameters(); IEnumerable <Type> toWrapArguments = toWrapInfo.GetParameters() // Closure argument isn't an actual argument, but added by the compiler for dynamically generated methods (expression trees). .SkipWhile(p => p.ParameterType.FullName == "System.Runtime.CompilerServices.Closure") .Select(p => p.ParameterType); ParameterConversionExpressions parameterExpressions = CreateParameterConversionExpressions(toCreateArguments, toWrapArguments); // Create call to wrapped delegate. Expression delegateCall = Expression.Invoke( Expression.Constant(toWrap), parameterExpressions.ConvertedParameters); return(Expression.Lambda <TDelegate>( ConvertOrWrapDelegate(delegateCall, toCreateInfo.ReturnType), parameterExpressions.OriginalParameters ).Compile()); }
/// <summary> /// Creates a delegate of a specified type that represents a method which can be executed on an instance passed as parameter. /// </summary> /// <typeparam name = "TDelegate"> /// The type for the delegate. This delegate needs at least one (first) type parameter denoting the type of the instance /// which will be passed. /// E.g., Action<ExampleObject, object>, /// where ExampleObject denotes the instance type and object denotes the desired type of the first parameter of the method. /// </typeparam> /// <param name = "method">The MethodInfo describing the method of the instance type.</param> /// <param name = "options">Options which specify what type of delegate should be created.</param> public static TDelegate CreateOpenInstanceDelegate <TDelegate>( MethodInfo method, CreateOptions options = CreateOptions.None) where TDelegate : class { if (method.IsStatic) { throw new ArgumentException("Since the delegate expects an instance, the method cannot be static.", nameof(method)); } switch (options) { case CreateOptions.None: // Ordinary delegate creation, maintaining variance safety. return(method.CreateDelegate(typeof(TDelegate)) as TDelegate); case CreateOptions.Downcasting: { MethodInfo delegateInfo = MethodInfoFromDelegateType(typeof(TDelegate)); ParameterInfo[] delegateParameters = delegateInfo.GetParameters(); // Convert instance type when necessary. Type delegateInstanceType = delegateParameters.Select(p => p.ParameterType).First(); Type methodInstanceType = method.DeclaringType; ParameterExpression instance = Expression.Parameter(delegateInstanceType); Expression convertedInstance = ConvertOrWrapDelegate(instance, methodInstanceType); // Create delegate original and converted arguments. IEnumerable <Type> delegateTypes = delegateParameters.Select(d => d.ParameterType).Skip(1); IEnumerable <Type> methodTypes = method.GetParameters().Select(m => m.ParameterType); ParameterConversionExpressions delegateParameterExpressions = CreateParameterConversionExpressions(delegateTypes, methodTypes); // Create method call. Expression methodCall = Expression.Call( convertedInstance, method, delegateParameterExpressions.ConvertedParameters); return(Expression.Lambda <TDelegate>( ConvertOrWrapDelegate(methodCall, delegateInfo.ReturnType), // Convert return type when necessary. new[] { instance }.Concat(delegateParameterExpressions.OriginalParameters) ).Compile()); } default: throw new NotSupportedException(); } }
/// <summary> /// Creates a delegate of a specified type that represents the specified static or instance method, /// with the specified first argument. /// </summary> /// <typeparam name = "TDelegate">The type for the delegate.</typeparam> /// <param name = "method">The MethodInfo describing the static or instance method the delegate is to represent.</param> /// <param name = "instance">When method is an instance method, the instance to call this method on. Null for static methods.</param> /// <param name = "options">Options which specify what type of delegate should be created.</param> public static TDelegate CreateDelegate <TDelegate>( MethodInfo method, object instance = null, CreateOptions options = CreateOptions.None) where TDelegate : class { switch (options) { case CreateOptions.None: // Ordinary delegate creation, maintaining variance safety. return(method.CreateDelegate(typeof(TDelegate), instance) as TDelegate); case CreateOptions.Downcasting: { MethodInfo delegateInfo = MethodInfoFromDelegateType(typeof(TDelegate)); // Create delegate original and converted arguments. IEnumerable <Type> delegateTypes = delegateInfo.GetParameters().Select(d => d.ParameterType); IEnumerable <Type> methodTypes = method.GetParameters().Select(p => p.ParameterType); ParameterConversionExpressions delegateParameterExpressions = CreateParameterConversionExpressions(delegateTypes, methodTypes); // Create method call. Expression methodCall = Expression.Call( instance == null ? null : Expression.Constant(instance), method, delegateParameterExpressions.ConvertedParameters); return(Expression.Lambda <TDelegate>( ConvertOrWrapDelegate(methodCall, delegateInfo.ReturnType), // Convert return type when necessary. delegateParameterExpressions.OriginalParameters ).Compile()); } default: throw new NotSupportedException(); } }