From 8441e0e97665a0766f8663f5b58d88ddaee69122 Mon Sep 17 00:00:00 2001 From: Olivier Spinelli Date: Mon, 18 Apr 2016 16:28:39 +0200 Subject: [PATCH] Refactored CK.Reflection: no more oject/type extension methods. Renamed ObjectAndTypeExtension to DelegateHelper that now supports CreateSetter methods. --- CK.Reflection/CK.Reflection.csproj | 3 +- ...tAndTypeExtension.cs => DelegateHelper.cs} | 864 +++++++++--------- .../CustomAttributeExtensions.cs | 253 ----- CK.Reflection/ReflectionHelper.cs | 74 +- CK.Text/CK.Text.csproj | 5 + CodeCakeBuilder/NuSpec/CK.Reflection.nuspec | 4 + Tests/CK.Reflection.Tests/HelperTest.cs | 8 +- .../CK.Reflection.Tests/ObjectAndTypeTest.cs | 42 +- 8 files changed, 493 insertions(+), 760 deletions(-) rename CK.Reflection/{ObjectAndTypeExtension.cs => DelegateHelper.cs} (83%) delete mode 100644 CK.Reflection/LegacySupport/CustomAttributeExtensions.cs diff --git a/CK.Reflection/CK.Reflection.csproj b/CK.Reflection/CK.Reflection.csproj index 86d7eaf9..0336edfb 100644 --- a/CK.Reflection/CK.Reflection.csproj +++ b/CK.Reflection/CK.Reflection.csproj @@ -51,9 +51,8 @@ - - + diff --git a/CK.Reflection/ObjectAndTypeExtension.cs b/CK.Reflection/DelegateHelper.cs similarity index 83% rename from CK.Reflection/ObjectAndTypeExtension.cs rename to CK.Reflection/DelegateHelper.cs index 19130330..eb7fee26 100644 --- a/CK.Reflection/ObjectAndTypeExtension.cs +++ b/CK.Reflection/DelegateHelper.cs @@ -1,410 +1,454 @@ -#region LGPL License -/*---------------------------------------------------------------------------- -* This file (CK.Reflection\ObjectAndTypeExtension.cs) is part of CiviKey. -* -* CiviKey is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* CiviKey is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* You should have received a copy of the GNU Lesser General Public License -* along with CiviKey. If not, see . -* -* Copyright © 2007-2015, -* Invenietis , -* In’Tech INFO , -* All rights reserved. -*-----------------------------------------------------------------------------*/ -#endregion - -#region Licence -/*---------------------------------------------------------------------------- - * Copyright (C) 2009 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *-----------------------------------------------------------------------------*/ -// -// Modified by Olivier Spinelli. feb. 12, 2011 -// -// - Changed the namespace. -// - Added overload with throwOnError (optional) parameter. -// - Removed XXXOrFail versions. -// - Integrated with CK.Reflection.Helper class methods (the ObjectAndTypeExtension class exposes only extension methods). -// -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Reflection; -using System.Reflection.Emit; - -namespace CK.Reflection -{ - /// - /// Utility methods for common reflection tasks. - /// Adapted from http://kennethxublogsource.googlecode.com/svn/trunk/CommonReflection/src/Common.Reflection/Reflections.cs - /// by Kenneth Xu. - /// - public static class ObjectAndTypeExtension - { - /// - /// Extension method to obtain a delegate of type that can be used to call the - /// static method with given method from given . The method signature must be compatible - /// with the parameter and return type of . - /// - /// Type of a .Net delegate. - /// The type to locate the compatible method. - /// The name of the method. - /// A delegate of type or null when no matching method if found. - public static TDelegate GetStaticInvoker( this Type type, string name ) - where TDelegate : class - { - return GetStaticInvoker( type, name, false ); - } - - /// - /// Extension method to obtain a delegate of type that can be used to call the - /// static method with given method from given . The method signature must be compatible - /// with the parameter and return type of . - /// - /// Type of a .Net delegate. - /// The type to locate the compatible method. - /// The name of the method. - /// True to raise a when not found. - /// A delegate of type or null if the method has not been found and is false. - /// When there is no matching method found and is true. - public static TDelegate GetStaticInvoker( this Type type, string name, bool throwOnError ) - where TDelegate : class - { - return new DelegateBuilder( type, name, throwOnError, false ).CreateInvoker(); - } - - /// - /// Extension method to obtain a delegate of type that can be used to call the - /// instance method with given method from given . The first parameter type of TDelegate - /// must be assignable to the given . The rest parameters and return type of TDelegate must be compatible with - /// the signature of the method. - /// - /// Type of a .Net delegate. - /// The type to locate the compatible method. - /// The name of the method. - /// A delegate of type or null when no matching method if found. - public static TDelegate GetInstanceInvoker( this Type type, string name ) - where TDelegate : class - { - return GetInstanceInvoker( type, name, false ); - } - - /// - /// Extension method to obtain a delegate of type that can be used to call the - /// instance method with given method from given . The first parameter type of TDelegate - /// must be assignable to the given . The rest parameters and return type of TDelegate must be compatible with - /// the signature of the method. - /// - /// Type of a .Net delegate. - /// The type to locate the compatible method. - /// The name of the method. - /// True to raise a when not found. - /// A delegate of type or null when no matching method if found and is false. - /// When there is no matching method found and is true. - public static TDelegate GetInstanceInvoker( this Type type, string name, bool throwOnError ) - where TDelegate : class - { - return new DelegateBuilder( type, name, throwOnError, true ).CreateInvoker(); - } - - /// - /// Extension method to obtain a delegate of type that can be used to call the - /// instance method with given method on specific object. - /// The method signature must be compatible with the signature of . - /// - /// Type of a .Net delegate. - /// The object instance to find the method. - /// The name of the method. - /// A delegate of type or null when no matching method if found. - public static TDelegate GetInstanceInvoker( this object obj, string name ) - where TDelegate : class - { - return GetInstanceInvoker( obj, name, false ); - } - - /// - /// Extension method to obtain a delegate of type that can be used to call the - /// instance method with given method on specific object. - /// The method signature must be compatible with the signature of . - /// - /// Type of a .Net delegate. - /// The object instance to find the method. - /// The name of the method. - /// True to raise a when not found. - /// A delegate of type or null when no matching method if found and is false. - /// When there is no matching method found and is true. - public static TDelegate GetInstanceInvoker( this object obj, string name, bool throwOnError ) - where TDelegate : class - { - return new DelegateBuilder( obj, obj.GetType(), name, throwOnError ).CreateInvoker(); - } - - /// - /// Extension method to obtain a delegate of type specified by parameter that can be used to make non virtual - /// call to instance method with given method on given . - /// The first parameter type of TDelegate must be assignable to the given . - /// Remaining parameters and return type of TDelegate must be compatible with the signature of the method. - /// - /// Type of a .Net delegate. - /// The type to locate the compatible method. - /// The name of the method. - /// A delegate of type or null when no matching method if found. - public static TDelegate GetNonVirtualInvoker( this Type type, string name ) - where TDelegate : class - { - return GetNonVirtualInvoker( type, name, false ); - } - - /// - /// Extension method to obtain a delegate of type specified by parameter that can be used to make non virtual - /// call to instance method with given method on given . - /// The first parameter type of TDelegate must be assignable to the given . - /// Remaining parameters and return type of TDelegate must be compatible with the signature of the method. - /// - /// Type of a .Net delegate. - /// The type to locate the compatible method. - /// The name of the method. - /// True to raise a when not found. - /// A delegate of type or null when no matching method if found and is false. - /// When there is no matching method found and is true. - public static TDelegate GetNonVirtualInvoker( this Type type, string name, bool throwOnError ) - where TDelegate : class - { - return new DelegateBuilder( type, name, throwOnError, true ).CreateInvoker( true ); - } - - /// - /// Extension method to obtain a delegate of type specified by parameter that can be used to make non virtual - /// call on the specific object to the instance method of given defined in the - /// given or its ancestor. - /// The method signature must be compatible with the signature of . - /// - /// Type of a .Net delegate. - /// The object instance to invoke the method. - /// The type to find the method. - /// The name of the method. - /// A delegate of type or null when no matching method if found. - public static TDelegate GetNonVirtualInvoker( this object obj, Type type, string name ) - where TDelegate : class - { - return GetNonVirtualInvoker( obj, type, name, false ); - } - - /// - /// Extension method to obtain a delegate of type specified by parameter that can be used to make non virtual - /// call on the specific object to the instance method of given defined in the - /// given or its ancestor. - /// The method signature must be compatible with the signature of . - /// - /// Type of a .Net delegate. - /// The object instance to invoke the method. - /// The type to find the method. - /// The name of the method. - /// True to raise a when not found. - /// A delegate of type or null when no matching method if found and is false. - /// When there is no matching method found and is true. - public static TDelegate GetNonVirtualInvoker( this object obj, Type type, string name, bool throwOnError ) - where TDelegate : class - { - return new DelegateBuilder( obj, type, name, throwOnError ).CreateInvoker( true ); - } - - /// - /// This is a more general purpose method to obtain a delegate of type specified by parameter that can - /// be used to call on the specific object to the method of given defined in the given - /// or its ancestor. The method signature must be compatible with the signature of . - /// - /// Type of a .Net delegate. - /// The object instance to invoke the method or null for static methods and open instance methods. - /// The type to find the method. - /// The name of the method. - /// A bitmask comprised of one or more that specify how the search is conducted. -or- Zero, to return null. - /// The additional filter to include/exclude methods. - /// The description of the additional filter criteria that will be included in the exception message when no matching method found. - /// True to raise a when not found. - /// A delegate of type . - /// When there is no matching method found. - public static TDelegate GetInvoker( object obj, Type type, string name, BindingFlags bindingAttr, Func filter, string filterMessage, bool throwOnError ) - where TDelegate : class - { - return new DelegateBuilder( obj, type, name, throwOnError, bindingAttr ) - { - MethodFilter = filter, - MethodFilterMessage = filterMessage - }.CreateInvoker(); - } - - - static DynamicMethod CreateDynamicMethod( MethodInfo m ) - { - var types = m.IsStatic ? ReflectionHelper.CreateParametersType( m.GetParameters() ) : ReflectionHelper.CreateParametersType( m.GetParameters(), m.DeclaringType ); - DynamicMethod dynamicMethod = new DynamicMethod( "NonVirtualInvoker_" + m.Name, m.ReturnType, types, m.DeclaringType ); - ILGenerator il = dynamicMethod.GetILGenerator(); - il.RepushActualParameters( true, types.Length ); - il.EmitCall( OpCodes.Call, m, null ); - il.Emit( OpCodes.Ret ); - return dynamicMethod; - } - - private class DelegateBuilder where T : class - { - #region Constants - private const BindingFlags ALL_STATIC_METHOD = - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ; //REVIEW: does no longer exists. BindingFlags.InvokeMethod; - - private const BindingFlags ALL_INSTANCE_METHOD = - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; //REVIEW: does no longer exists. BindingFlags.InvokeMethod; - #endregion - - private readonly bool _throwOnError; - private readonly string _methodName; - private readonly Type _targetType; - private readonly object _targetObject; - private readonly BindingFlags _bindingAttr; - private Type _returnType; - private Type[] _parameterTypes; - - internal Func MethodFilter { get; set; } - internal string MethodFilterMessage { get; set; } - - public DelegateBuilder( object targetObject, Type targetType, string methodName, bool throwOnError ) - : this( targetObject, targetType, methodName, throwOnError, ALL_INSTANCE_METHOD ) - { - } - - public DelegateBuilder( Type targetType, string methodName, bool throwOnError, bool isInstanceMethod ) - : this( null, targetType, methodName, throwOnError, isInstanceMethod ? ALL_INSTANCE_METHOD : ALL_STATIC_METHOD ) - { - } - - internal DelegateBuilder( object targetObject, Type targetType, string methodName, bool throwOnError, BindingFlags bindingAttr ) - { - if( !typeof( Delegate ).GetTypeInfo().IsAssignableFrom( typeof( T ).GetTypeInfo() ) ) - { - throw new ArgumentException( "Expecting type parameter to be a Delegate type, but got " + typeof( T ).FullName ); - } - _targetObject = targetObject; - _targetType = targetType; - _methodName = methodName; - _throwOnError = throwOnError; - _bindingAttr = bindingAttr; - } - - public T CreateInvoker() - { - return CreateInvoker( false ); - } - - public T CreateInvoker( bool nonVirtual ) - { - MethodInfo method; - if( (method = GetMethod()) == null ) return null; - try - { - if( nonVirtual && method.IsVirtual ) - { - var dynamicMethod = CreateDynamicMethod( method ); - return _targetObject == null ? - dynamicMethod.CreateDelegate( typeof( T ) ) as T : - dynamicMethod.CreateDelegate( typeof( T ), _targetObject ) as T; - } - return _targetObject == null ? method.CreateDelegate( typeof( T ) ) as T : method.CreateDelegate( typeof( T ), _targetObject ) as T; - } - catch( ArgumentException ex ) - { - if( _throwOnError ) throw new MissingMethodException( BuildExceptionMessage(), ex ); - return null; - } - } - - private MethodInfo GetMethod() - { - MethodInfo invokeMethod = typeof( T ).GetMethod( "Invoke" ); - ParameterInfo[] parameters = invokeMethod.GetParameters(); - _returnType = invokeMethod.ReturnType; - - bool instanceToStatic = (_targetObject == null && _bindingAttr == ALL_INSTANCE_METHOD); - if( instanceToStatic ) - { - if( parameters.Length == 0 ) - { - throw new InvalidOperationException( string.Format( - "Delegate {0} has no parameter. It is required to have at least one parameter that is assignable from target type.", - typeof( T ) ) ); - } - Type instanceType = parameters[0].ParameterType; - if( !_targetType.IsAssignableFrom( instanceType ) ) - { - if( _throwOnError ) - { - throw new MissingMethodException( string.Format( - "Target type {0} is not assignable to the first parameter of delegate {1}.", - _targetType, instanceType ) ); - } - return null; - } - } - _parameterTypes = instanceToStatic ? ReflectionHelper.CreateParametersType( parameters, 1 ) : ReflectionHelper.CreateParametersType( parameters ); - - var method = _targetType.GetMethod( _methodName, _parameterTypes ); - var methodFilter = MethodFilter; - if( method != null && methodFilter != null && !methodFilter( method ) ) - { - method = null; - } - if( method == null && _throwOnError ) - { - throw new MissingMethodException( BuildExceptionMessage() ); - } - return method; - } - - private string BuildExceptionMessage() - { - StringBuilder sb = new StringBuilder() - .Append( "No matching method found in the type " ) - .Append( _targetType ) - .Append( " for signature " ) - .Append( _returnType ).Append( " " ) - .Append( _methodName ).Append( "(" ); - if( _parameterTypes.Length > 0 ) - { - foreach( Type parameter in _parameterTypes ) - { - sb.Append( parameter ).Append( ", " ); - } - sb.Length -= 2; - } - sb.Append( ") with binding flags: " ).Append( _bindingAttr ); - if( MethodFilter != null ) - { - sb.Append( " with filter " ).Append( MethodFilterMessage ?? MethodFilter.ToString() ); - } - sb.Append( "." ); - return sb.ToString(); - } - } - } -} +#region Licence +/*---------------------------------------------------------------------------- + * Copyright (C) 2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *-----------------------------------------------------------------------------*/ +// +// Modified by Olivier Spinelli. feb. 12, 2011 +// +// - Changed the namespace. +// - Added overload with throwOnError (optional) parameter. +// - Removed XXXOrFail versions. +// - Renamed to DelegateHelper. +// +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using System.Reflection.Emit; +using System.Linq.Expressions; + +namespace CK.Reflection +{ + /// + /// Utility methods for common reflection tasks. + /// Adapted from http://kennethxublogsource.googlecode.com/svn/trunk/CommonReflection/src/Common.Reflection/Reflections.cs + /// by Kenneth Xu. + /// + public static class DelegateHelper + { + /// + /// Describes the behavior of methods when no setter exists + /// on the property. + /// + public enum CreateInvalidSetterOption + { + /// + /// Throws an . This is the default. + /// + ThrowException, + /// + /// Returns a null action delegate. + /// + NullAction, + /// + /// Returns a void action (an action that does nothing). + /// + VoidAction + } + + + /// + /// Creates a setter for a property. + /// + /// Property holder type (will be inferred by the compiler). + /// Property type (will be inferred by the compiler). + /// An instance of the . + /// A lambda function that selects the property. + /// Error handling options. Defaults to . + /// An action that takes an holder instance and the value to set. + public static Action CreateSetter( THolder source, Expression> propertyLambda, CreateInvalidSetterOption o = CreateInvalidSetterOption.ThrowException ) + { + return CreateSetter( ReflectionHelper.DoGetPropertyInfo( propertyLambda ), o ); + } + + /// + /// Creates a setter fo a property. + /// + /// Property holder type. + /// Property type. + /// A lambda function that selects the property. + /// Error handling options. Defaults to . + /// An action that takes an holder instance and the value to set. + public static Action CreateSetter( Expression> propertyLambda, CreateInvalidSetterOption o = CreateInvalidSetterOption.ThrowException ) + { + return CreateSetter( ReflectionHelper.DoGetPropertyInfo( propertyLambda ), o ); + } + + private static Action CreateSetter( PropertyInfo property, CreateInvalidSetterOption o ) + { + var holderType = Expression.Parameter( typeof( THolder ), "e" ); + var propType = Expression.Parameter( typeof( TProperty ), "v" ); + MethodInfo s = property.GetSetMethod(); + if( s == null ) + { + if( o == CreateInvalidSetterOption.ThrowException ) throw new InvalidOperationException( string.Format( "Property '{0}' has no setter.", property.Name ) ); + if( o == CreateInvalidSetterOption.NullAction ) return null; + return VoidAction; + } + return (Action)s.CreateDelegate( typeof( Action ) ); + } + + static void VoidAction( T1 o1, T2 o2 ) + { + } + + /// + /// Extension method to obtain a delegate of type that can be used to call the + /// static method with given method from given . The method signature must be compatible + /// with the parameter and return type of . + /// + /// Type of a .Net delegate. + /// The type to locate the compatible method. + /// The name of the method. + /// A delegate of type or null when no matching method if found. + public static TDelegate GetStaticInvoker( Type type, string name ) + where TDelegate : class + { + return GetStaticInvoker( type, name, false ); + } + + /// + /// Extension method to obtain a delegate of type that can be used to call the + /// static method with given method from given . The method signature must be compatible + /// with the parameter and return type of . + /// + /// Type of a .Net delegate. + /// The type to locate the compatible method. + /// The name of the method. + /// True to raise a when not found. + /// A delegate of type or null if the method has not been found and is false. + /// When there is no matching method found and is true. + public static TDelegate GetStaticInvoker( Type type, string name, bool throwOnError ) + where TDelegate : class + { + return new DelegateBuilder( type, name, throwOnError, false ).CreateInvoker(); + } + + /// + /// Extension method to obtain a delegate of type that can be used to call the + /// instance method with given method from given . The first parameter type of TDelegate + /// must be assignable to the given . The rest parameters and return type of TDelegate must be compatible with + /// the signature of the method. + /// + /// Type of a .Net delegate. + /// The type to locate the compatible method. + /// The name of the method. + /// A delegate of type or null when no matching method if found. + public static TDelegate GetInstanceInvoker( Type type, string name ) + where TDelegate : class + { + return GetInstanceInvoker( type, name, false ); + } + + /// + /// Extension method to obtain a delegate of type that can be used to call the + /// instance method with given method from given . The first parameter type of TDelegate + /// must be assignable to the given . The rest parameters and return type of TDelegate must be compatible with + /// the signature of the method. + /// + /// Type of a .Net delegate. + /// The type to locate the compatible method. + /// The name of the method. + /// True to raise a when not found. + /// A delegate of type or null when no matching method if found and is false. + /// When there is no matching method found and is true. + public static TDelegate GetInstanceInvoker( Type type, string name, bool throwOnError ) + where TDelegate : class + { + return new DelegateBuilder( type, name, throwOnError, true ).CreateInvoker(); + } + + /// + /// Extension method to obtain a delegate of type that can be used to call the + /// instance method with given method on specific object. + /// The method signature must be compatible with the signature of . + /// + /// Type of a .Net delegate. + /// The object instance to find the method. + /// The name of the method. + /// A delegate of type or null when no matching method if found. + public static TDelegate GetInstanceInvoker( object obj, string name ) + where TDelegate : class + { + return GetInstanceInvoker( obj, name, false ); + } + + /// + /// Extension method to obtain a delegate of type that can be used to call the + /// instance method with given method on specific object. + /// The method signature must be compatible with the signature of . + /// + /// Type of a .Net delegate. + /// The object instance to find the method. + /// The name of the method. + /// True to raise a when not found. + /// A delegate of type or null when no matching method if found and is false. + /// When there is no matching method found and is true. + public static TDelegate GetInstanceInvoker( object obj, string name, bool throwOnError ) + where TDelegate : class + { + return new DelegateBuilder( obj, obj.GetType(), name, throwOnError ).CreateInvoker(); + } + + /// + /// Extension method to obtain a delegate of type specified by parameter that can be used to make non virtual + /// call to instance method with given method on given . + /// The first parameter type of TDelegate must be assignable to the given . + /// Remaining parameters and return type of TDelegate must be compatible with the signature of the method. + /// + /// Type of a .Net delegate. + /// The type to locate the compatible method. + /// The name of the method. + /// A delegate of type or null when no matching method if found. + public static TDelegate GetNonVirtualInvoker( Type type, string name ) + where TDelegate : class + { + return GetNonVirtualInvoker( type, name, false ); + } + + /// + /// Extension method to obtain a delegate of type specified by parameter that can be used to make non virtual + /// call to instance method with given method on given . + /// The first parameter type of TDelegate must be assignable to the given . + /// Remaining parameters and return type of TDelegate must be compatible with the signature of the method. + /// + /// Type of a .Net delegate. + /// The type to locate the compatible method. + /// The name of the method. + /// True to raise a when not found. + /// A delegate of type or null when no matching method if found and is false. + /// When there is no matching method found and is true. + public static TDelegate GetNonVirtualInvoker( Type type, string name, bool throwOnError ) + where TDelegate : class + { + return new DelegateBuilder( type, name, throwOnError, true ).CreateInvoker( true ); + } + + /// + /// Extension method to obtain a delegate of type specified by parameter that can be used to make non virtual + /// call on the specific object to the instance method of given defined in the + /// given or its ancestor. + /// The method signature must be compatible with the signature of . + /// + /// Type of a .Net delegate. + /// The object instance to invoke the method. + /// The type to find the method. + /// The name of the method. + /// A delegate of type or null when no matching method if found. + public static TDelegate GetNonVirtualInvoker( object obj, Type type, string name ) + where TDelegate : class + { + return GetNonVirtualInvoker( obj, type, name, false ); + } + + /// + /// Extension method to obtain a delegate of type specified by parameter that can be used to make non virtual + /// call on the specific object to the instance method of given defined in the + /// given or its ancestor. + /// The method signature must be compatible with the signature of . + /// + /// Type of a .Net delegate. + /// The object instance to invoke the method. + /// The type to find the method. + /// The name of the method. + /// True to raise a when not found. + /// A delegate of type or null when no matching method if found and is false. + /// When there is no matching method found and is true. + public static TDelegate GetNonVirtualInvoker( object obj, Type type, string name, bool throwOnError ) + where TDelegate : class + { + return new DelegateBuilder( obj, type, name, throwOnError ).CreateInvoker( true ); + } + + /// + /// This is a more general purpose method to obtain a delegate of type specified by parameter that can + /// be used to call on the specific object to the method of given defined in the given + /// or its ancestor. The method signature must be compatible with the signature of . + /// + /// Type of a .Net delegate. + /// The object instance to invoke the method or null for static methods and open instance methods. + /// The type to find the method. + /// The name of the method. + /// A bitmask comprised of one or more that specify how the search is conducted. -or- Zero, to return null. + /// The additional filter to include/exclude methods. + /// The description of the additional filter criteria that will be included in the exception message when no matching method found. + /// True to raise a when not found. + /// A delegate of type . + /// When there is no matching method found. + public static TDelegate GetInvoker( object obj, Type type, string name, BindingFlags bindingAttr, Func filter, string filterMessage, bool throwOnError ) + where TDelegate : class + { + return new DelegateBuilder( obj, type, name, throwOnError, bindingAttr ) + { + MethodFilter = filter, + MethodFilterMessage = filterMessage + }.CreateInvoker(); + } + + + static DynamicMethod CreateDynamicMethod( MethodInfo m ) + { + var types = m.IsStatic ? ReflectionHelper.CreateParametersType( m.GetParameters() ) : ReflectionHelper.CreateParametersType( m.GetParameters(), m.DeclaringType ); + DynamicMethod dynamicMethod = new DynamicMethod( "NonVirtualInvoker_" + m.Name, m.ReturnType, types, m.DeclaringType ); + ILGenerator il = dynamicMethod.GetILGenerator(); + il.RepushActualParameters( true, types.Length ); + il.EmitCall( OpCodes.Call, m, null ); + il.Emit( OpCodes.Ret ); + return dynamicMethod; + } + + private class DelegateBuilder where T : class + { + #region Constants + private const BindingFlags ALL_STATIC_METHOD = + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ; //REVIEW: does no longer exists. BindingFlags.InvokeMethod; + + private const BindingFlags ALL_INSTANCE_METHOD = + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; //REVIEW: does no longer exists. BindingFlags.InvokeMethod; + #endregion + + private readonly bool _throwOnError; + private readonly string _methodName; + private readonly Type _targetType; + private readonly object _targetObject; + private readonly BindingFlags _bindingAttr; + private Type _returnType; + private Type[] _parameterTypes; + + internal Func MethodFilter { get; set; } + internal string MethodFilterMessage { get; set; } + + public DelegateBuilder( object targetObject, Type targetType, string methodName, bool throwOnError ) + : this( targetObject, targetType, methodName, throwOnError, ALL_INSTANCE_METHOD ) + { + } + + public DelegateBuilder( Type targetType, string methodName, bool throwOnError, bool isInstanceMethod ) + : this( null, targetType, methodName, throwOnError, isInstanceMethod ? ALL_INSTANCE_METHOD : ALL_STATIC_METHOD ) + { + } + + internal DelegateBuilder( object targetObject, Type targetType, string methodName, bool throwOnError, BindingFlags bindingAttr ) + { + if( !typeof( Delegate ).GetTypeInfo().IsAssignableFrom( typeof( T ).GetTypeInfo() ) ) + { + throw new ArgumentException( "Expecting type parameter to be a Delegate type, but got " + typeof( T ).FullName ); + } + _targetObject = targetObject; + _targetType = targetType; + _methodName = methodName; + _throwOnError = throwOnError; + _bindingAttr = bindingAttr; + } + + public T CreateInvoker() + { + return CreateInvoker( false ); + } + + public T CreateInvoker( bool nonVirtual ) + { + MethodInfo method; + if( (method = GetMethod()) == null ) return null; + try + { + if( nonVirtual && method.IsVirtual ) + { + var dynamicMethod = CreateDynamicMethod( method ); + return _targetObject == null ? + dynamicMethod.CreateDelegate( typeof( T ) ) as T : + dynamicMethod.CreateDelegate( typeof( T ), _targetObject ) as T; + } + return _targetObject == null ? method.CreateDelegate( typeof( T ) ) as T : method.CreateDelegate( typeof( T ), _targetObject ) as T; + } + catch( ArgumentException ex ) + { + if( _throwOnError ) throw new MissingMethodException( BuildExceptionMessage(), ex ); + return null; + } + } + + private MethodInfo GetMethod() + { + MethodInfo invokeMethod = typeof( T ).GetMethod( "Invoke" ); + ParameterInfo[] parameters = invokeMethod.GetParameters(); + _returnType = invokeMethod.ReturnType; + + bool instanceToStatic = (_targetObject == null && _bindingAttr == ALL_INSTANCE_METHOD); + if( instanceToStatic ) + { + if( parameters.Length == 0 ) + { + throw new InvalidOperationException( string.Format( + "Delegate {0} has no parameter. It is required to have at least one parameter that is assignable from target type.", + typeof( T ) ) ); + } + Type instanceType = parameters[0].ParameterType; + if( !_targetType.IsAssignableFrom( instanceType ) ) + { + if( _throwOnError ) + { + throw new MissingMethodException( string.Format( + "Target type {0} is not assignable to the first parameter of delegate {1}.", + _targetType, instanceType ) ); + } + return null; + } + } + _parameterTypes = instanceToStatic ? ReflectionHelper.CreateParametersType( parameters, 1 ) : ReflectionHelper.CreateParametersType( parameters ); + + var method = _targetType.GetMethod( _methodName, _parameterTypes ); + var methodFilter = MethodFilter; + if( method != null && methodFilter != null && !methodFilter( method ) ) + { + method = null; + } + if( method == null && _throwOnError ) + { + throw new MissingMethodException( BuildExceptionMessage() ); + } + return method; + } + + private string BuildExceptionMessage() + { + StringBuilder sb = new StringBuilder() + .Append( "No matching method found in the type " ) + .Append( _targetType ) + .Append( " for signature " ) + .Append( _returnType ).Append( " " ) + .Append( _methodName ).Append( "(" ); + if( _parameterTypes.Length > 0 ) + { + foreach( Type parameter in _parameterTypes ) + { + sb.Append( parameter ).Append( ", " ); + } + sb.Length -= 2; + } + sb.Append( ") with binding flags: " ).Append( _bindingAttr ); + if( MethodFilter != null ) + { + sb.Append( " with filter " ).Append( MethodFilterMessage ?? MethodFilter.ToString() ); + } + sb.Append( "." ); + return sb.ToString(); + } + } + } +} diff --git a/CK.Reflection/LegacySupport/CustomAttributeExtensions.cs b/CK.Reflection/LegacySupport/CustomAttributeExtensions.cs deleted file mode 100644 index b55cdb64..00000000 --- a/CK.Reflection/LegacySupport/CustomAttributeExtensions.cs +++ /dev/null @@ -1,253 +0,0 @@ -#region LGPL License -/*---------------------------------------------------------------------------- -* This file (CK.Reflection\LegacySupport\CustomAttributeExtensions.cs) is part of CiviKey. -* -* CiviKey is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published -* by the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* CiviKey is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* You should have received a copy of the GNU Lesser General Public License -* along with CiviKey. If not, see . -* -* Copyright © 2007-2015, -* Invenietis , -* In’Tech INFO , -* All rights reserved. -*-----------------------------------------------------------------------------*/ -#endregion - -#if net40 - -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace CK.Reflection -{ - - public static class CustomAttributeExtensions - { - - public static T GetCustomAttribute( this Assembly element ) where T : Attribute - { - return (T)element.GetCustomAttribute( typeof( T ) ); - } - - - public static T GetCustomAttribute( this MemberInfo element ) where T : Attribute - { - return (T)element.GetCustomAttribute( typeof( T ) ); - } - - - public static T GetCustomAttribute( this Module element ) where T : Attribute - { - return (T)element.GetCustomAttribute( typeof( T ) ); - } - - - public static T GetCustomAttribute( this ParameterInfo element ) where T : Attribute - { - return (T)element.GetCustomAttribute( typeof( T ) ); - } - - - public static Attribute GetCustomAttribute( this Assembly element, Type attributeType ) - { - return Attribute.GetCustomAttribute( element, attributeType ); - } - - - public static T GetCustomAttribute( this MemberInfo element, bool inherit ) where T : Attribute - { - return (T)element.GetCustomAttribute( typeof( T ), inherit ); - } - - - public static Attribute GetCustomAttribute( this MemberInfo element, Type attributeType ) - { - return Attribute.GetCustomAttribute( element, attributeType ); - } - - - public static Attribute GetCustomAttribute( this Module element, Type attributeType ) - { - return Attribute.GetCustomAttribute( element, attributeType ); - } - - - public static T GetCustomAttribute( this ParameterInfo element, bool inherit ) where T : Attribute - { - return (T)element.GetCustomAttribute( typeof( T ), inherit ); - } - - - public static Attribute GetCustomAttribute( this ParameterInfo element, Type attributeType ) - { - return Attribute.GetCustomAttribute( element, attributeType ); - } - - - public static Attribute GetCustomAttribute( this MemberInfo element, Type attributeType, bool inherit ) - { - return Attribute.GetCustomAttribute( element, attributeType, inherit ); - } - - - public static Attribute GetCustomAttribute( this ParameterInfo element, Type attributeType, bool inherit ) - { - return Attribute.GetCustomAttribute( element, attributeType, inherit ); - } - - - public static IEnumerable GetCustomAttributes( this Assembly element ) - { - return Attribute.GetCustomAttributes( element ); - } - - - public static IEnumerable GetCustomAttributes( this Assembly element ) where T : Attribute - { - return (IEnumerable)element.GetCustomAttributes( typeof( T ) ); - } - - - public static IEnumerable GetCustomAttributes( this MemberInfo element ) - { - return Attribute.GetCustomAttributes( element ); - } - - - public static IEnumerable GetCustomAttributes( this MemberInfo element ) where T : Attribute - { - return (IEnumerable)element.GetCustomAttributes( typeof( T ) ); - } - - - public static IEnumerable GetCustomAttributes( this Module element ) - { - return Attribute.GetCustomAttributes( element ); - } - - - public static IEnumerable GetCustomAttributes( this Module element ) where T : Attribute - { - return (IEnumerable)element.GetCustomAttributes( typeof( T ) ); - } - - - public static IEnumerable GetCustomAttributes( this ParameterInfo element ) - { - return Attribute.GetCustomAttributes( element ); - } - - - public static IEnumerable GetCustomAttributes( this ParameterInfo element ) where T : Attribute - { - return (IEnumerable)element.GetCustomAttributes( typeof( T ) ); - } - - - public static IEnumerable GetCustomAttributes( this Assembly element, Type attributeType ) - { - return Attribute.GetCustomAttributes( element, attributeType ); - } - - - public static IEnumerable GetCustomAttributes( this MemberInfo element, bool inherit ) - { - return Attribute.GetCustomAttributes( element, inherit ); - } - - - public static IEnumerable GetCustomAttributes( this MemberInfo element, bool inherit ) where T : Attribute - { - return (IEnumerable)element.GetCustomAttributes( typeof( T ), inherit ); - } - - - public static IEnumerable GetCustomAttributes( this MemberInfo element, Type attributeType ) - { - return Attribute.GetCustomAttributes( element, attributeType ); - } - - - public static IEnumerable GetCustomAttributes( this Module element, Type attributeType ) - { - return Attribute.GetCustomAttributes( element, attributeType ); - } - - - public static IEnumerable GetCustomAttributes( this ParameterInfo element, bool inherit ) - { - return Attribute.GetCustomAttributes( element, inherit ); - } - - - public static IEnumerable GetCustomAttributes( this ParameterInfo element, bool inherit ) where T : Attribute - { - return (IEnumerable)element.GetCustomAttributes( typeof( T ), inherit ); - } - - - public static IEnumerable GetCustomAttributes( this ParameterInfo element, Type attributeType ) - { - return Attribute.GetCustomAttributes( element, attributeType ); - } - - - public static IEnumerable GetCustomAttributes( this MemberInfo element, Type attributeType, bool inherit ) - { - return Attribute.GetCustomAttributes( element, attributeType, inherit ); - } - - - public static IEnumerable GetCustomAttributes( this ParameterInfo element, Type attributeType, bool inherit ) - { - return Attribute.GetCustomAttributes( element, attributeType, inherit ); - } - - - public static bool IsDefined( this Assembly element, Type attributeType ) - { - return Attribute.IsDefined( element, attributeType ); - } - - - public static bool IsDefined( this MemberInfo element, Type attributeType ) - { - return Attribute.IsDefined( element, attributeType ); - } - - - public static bool IsDefined( this Module element, Type attributeType ) - { - return Attribute.IsDefined( element, attributeType ); - } - - - public static bool IsDefined( this ParameterInfo element, Type attributeType ) - { - return Attribute.IsDefined( element, attributeType ); - } - - - public static bool IsDefined( this MemberInfo element, Type attributeType, bool inherit ) - { - return Attribute.IsDefined( element, attributeType, inherit ); - } - - - public static bool IsDefined( this ParameterInfo element, Type attributeType, bool inherit ) - { - return Attribute.IsDefined( element, attributeType, inherit ); - } - } -} - -#endif \ No newline at end of file diff --git a/CK.Reflection/ReflectionHelper.cs b/CK.Reflection/ReflectionHelper.cs index baddbbdc..af7a9004 100644 --- a/CK.Reflection/ReflectionHelper.cs +++ b/CK.Reflection/ReflectionHelper.cs @@ -37,26 +37,6 @@ namespace CK.Reflection /// static public class ReflectionHelper { - /// - /// Describes the behavior of methods when no setter exists - /// on the property. - /// - public enum CreateInvalidSetterOption - { - /// - /// Throws an . This is the default. - /// - ThrowException, - /// - /// Returns a null action delegate. - /// - NullAction, - /// - /// Returns a void action (an action that does nothing). - /// - VoidAction - } - /// /// Retrieves a from a lambda function based on an instance of the holder. /// @@ -70,20 +50,6 @@ public static PropertyInfo GetPropertyInfo( THolder source, return DoGetPropertyInfo( propertyLambda ); } - /// - /// Creates a setter for a property. - /// - /// Property holder type (will be inferred by the compiler). - /// Property type (will be inferred by the compiler). - /// An instance of the . - /// A lambda function that selects the property. - /// Error handling options. Defaults to . - /// An action that takes an holder instance and the value to set. - public static Action CreateSetter( THolder source, Expression> propertyLambda, CreateInvalidSetterOption o = CreateInvalidSetterOption.ThrowException ) - { - return CreateSetter( DoGetPropertyInfo( propertyLambda ), o ); - } - /// /// Retrieves a from a lambda function without requiring an instance of the holder /// object and without any constraint for the type of the property. @@ -108,19 +74,6 @@ public static PropertyInfo GetPropertyInfo( Expression - /// Creates a setter fo a property. - /// - /// Property holder type. - /// Property type. - /// A lambda function that selects the property. - /// Error handling options. Defaults to . - /// An action that takes an holder instance and the value to set. - public static Action CreateSetter( Expression> propertyLambda, CreateInvalidSetterOption o = CreateInvalidSetterOption.ThrowException ) - { - return CreateSetter( DoGetPropertyInfo( propertyLambda ), o ); - } - /// /// Retrieves a from a parameterless lambda function: a closure is actually required /// and the compiler generates one automatically. @@ -133,7 +86,7 @@ public static PropertyInfo GetPropertyInfo( Expression CreateSetter( PropertyInfo property, CreateInvalidSetterOption o ) - { - var holderType = Expression.Parameter( typeof( THolder ), "e" ); - var propType = Expression.Parameter( typeof( TProperty ), "v" ); - MethodInfo s = property.GetSetMethod(); - if( s == null ) - { - if( o == CreateInvalidSetterOption.ThrowException ) throw new InvalidOperationException( string.Format( "Property '{0}' has no setter.", property.Name ) ); - if( o == CreateInvalidSetterOption.NullAction ) return null; - return VoidAction; - } - return (Action)s.CreateDelegate( typeof( Action ) ); - } - - static void VoidAction( T1 o1, T2 o2 ) - { - } - - /// /// Creates an array of type of a method parameters. /// @@ -257,12 +191,12 @@ public static void GenerateCustomAttributeBuilder( IEnumerable ctorArgs) + private static bool ConstructorSignatureMatch( ConstructorInfo c, IList ctorArgs ) { var ctorParameters = c.GetParameters().ToList(); if( ctorParameters.Count != ctorArgs.Count ) return false; - - for (int i = 0; i < ctorParameters.Count; ++i ) + + for( int i = 0; i < ctorParameters.Count; ++i ) { if( ctorParameters[i].ParameterType != ctorArgs[i].ArgumentType ) return false; } diff --git a/CK.Text/CK.Text.csproj b/CK.Text/CK.Text.csproj index 70466f49..6eb127de 100644 --- a/CK.Text/CK.Text.csproj +++ b/CK.Text/CK.Text.csproj @@ -30,6 +30,10 @@ 4 bin\Release\CK.Text.xml + + + bin\$(Configuration)\$(AssemblyName).xml + true ..\SharedKey.snk @@ -40,6 +44,7 @@ Properties\SharedAssemblyInfo.cs + diff --git a/CodeCakeBuilder/NuSpec/CK.Reflection.nuspec b/CodeCakeBuilder/NuSpec/CK.Reflection.nuspec index a2d8640f..809a5d00 100644 --- a/CodeCakeBuilder/NuSpec/CK.Reflection.nuspec +++ b/CodeCakeBuilder/NuSpec/CK.Reflection.nuspec @@ -12,6 +12,10 @@ Reflection related utility classes. ({{CSemVer}}) + + Standard reflection and emit helpers: DelegateHelper allows invoking virtual method base, EmitHelper and ILGeneratorExtension + ease classical emit funtions. + en-US diff --git a/Tests/CK.Reflection.Tests/HelperTest.cs b/Tests/CK.Reflection.Tests/HelperTest.cs index a8bf3932..5ca19d00 100644 --- a/Tests/CK.Reflection.Tests/HelperTest.cs +++ b/Tests/CK.Reflection.Tests/HelperTest.cs @@ -116,14 +116,14 @@ public void PropertySetter() { { string s = "a string"; - Assert.Throws( () => ReflectionHelper.CreateSetter( s, x => x.Length ) ); - Assert.That( ReflectionHelper.CreateSetter( s, x => x.Length, ReflectionHelper.CreateInvalidSetterOption.NullAction ), Is.Null ); - var p = ReflectionHelper.CreateSetter( s, x => x.Length, ReflectionHelper.CreateInvalidSetterOption.VoidAction ); + Assert.Throws( () => DelegateHelper.CreateSetter( s, x => x.Length ) ); + Assert.That( DelegateHelper.CreateSetter( s, x => x.Length, DelegateHelper.CreateInvalidSetterOption.NullAction ), Is.Null ); + var p = DelegateHelper.CreateSetter( s, x => x.Length, DelegateHelper.CreateInvalidSetterOption.VoidAction ); p( s, 4554 ); } { System.IO.StringWriter a = new System.IO.StringWriter(); - var setter = ReflectionHelper.CreateSetter( a, x => x.NewLine ); + var setter = DelegateHelper.CreateSetter( a, x => x.NewLine ); Assert.That( a.NewLine, Is.EqualTo( Environment.NewLine ) ); setter( a, "Hello World!" ); Assert.That( a.NewLine, Is.EqualTo( "Hello World!" ) ); diff --git a/Tests/CK.Reflection.Tests/ObjectAndTypeTest.cs b/Tests/CK.Reflection.Tests/ObjectAndTypeTest.cs index 47aa03ed..e4f49e61 100644 --- a/Tests/CK.Reflection.Tests/ObjectAndTypeTest.cs +++ b/Tests/CK.Reflection.Tests/ObjectAndTypeTest.cs @@ -74,20 +74,20 @@ public void StaticInvoker() { { // Null or MissingMethodException - Func fsUnk1 = tA.GetStaticInvoker>( "StaticMethod" ); + Func fsUnk1 = DelegateHelper.GetStaticInvoker>( tA, "StaticMethod" ); Assert.That( fsUnk1, Is.Null ); - Func fsUnk2 = tA.GetStaticInvoker>( "StaticMethodUnk" ); + Func fsUnk2 = DelegateHelper.GetStaticInvoker>( tA, "StaticMethodUnk" ); Assert.That( fsUnk2, Is.Null ); - Assert.Throws( () => tA.GetStaticInvoker>( "StaticMethod", true ) ); - Assert.Throws( () => tA.GetStaticInvoker>( "StaticMethodUnk", true ) ); + Assert.Throws( () => DelegateHelper.GetStaticInvoker>( tA, "StaticMethod", true ) ); + Assert.Throws( () => DelegateHelper.GetStaticInvoker>( tA, "StaticMethodUnk", true ) ); - Assert.That( tA.GetStaticInvoker>( "StaticMethod", false ), Is.Null ); - Assert.That( tA.GetStaticInvoker>( "StaticMethodUnk", false ), Is.Null ); + Assert.That( DelegateHelper.GetStaticInvoker>( tA, "StaticMethod", false ), Is.Null ); + Assert.That( DelegateHelper.GetStaticInvoker>( tA, "StaticMethodUnk", false ), Is.Null ); } // Delegate to the static method. - Func fsA = tA.GetStaticInvoker>( "StaticMethod" ); + Func fsA = DelegateHelper.GetStaticInvoker>( tA, "StaticMethod" ); Assert.That( fsA, Is.Not.Null ); Assert.That( fsA( 1 ), Is.EqualTo( "1" ) ); Assert.That( _lastCalledName, Is.EqualTo( "A.StaticMethod" ) ); @@ -104,23 +104,23 @@ public void InstanceInvoker() { // Null or MissingMethodException. - Func fUnk1 = tA.GetInstanceInvoker>( "SimpleMethod" ); + Func fUnk1 = DelegateHelper.GetInstanceInvoker>( tA, "SimpleMethod" ); Assert.That( fUnk1, Is.Null ); - Func fUnk2 = tA.GetInstanceInvoker>( "SimpleMethoddUnk" ); + Func fUnk2 = DelegateHelper.GetInstanceInvoker>( tA, "SimpleMethoddUnk" ); Assert.That( fUnk2, Is.Null ); - Assert.Throws( () => tA.GetInstanceInvoker>( "SimpleMethod", true ) ); - Assert.Throws( () => tA.GetInstanceInvoker>( "SimpleMethodUnk", true ) ); + Assert.Throws( () => DelegateHelper.GetInstanceInvoker>( tA, "SimpleMethod", true ) ); + Assert.Throws( () => DelegateHelper.GetInstanceInvoker>( tA, "SimpleMethodUnk", true ) ); - Assert.That( tA.GetInstanceInvoker>( "SimpleMethod", false ), Is.Null ); - Assert.That( tA.GetInstanceInvoker>( "SimpleMethodUnk", false ), Is.Null ); + Assert.That( DelegateHelper.GetInstanceInvoker>( tA, "SimpleMethod", false ), Is.Null ); + Assert.That( DelegateHelper.GetInstanceInvoker>( tA, "SimpleMethodUnk", false ), Is.Null ); } A a = new A(); B b = new B(); { - Func fA = tA.GetInstanceInvoker>( "SimpleMethod" ); + Func fA = DelegateHelper.GetInstanceInvoker>( tA, "SimpleMethod" ); Assert.That( fA( a, 2 ), Is.EqualTo( "2" ) ); Assert.That( _lastCalledName, Is.EqualTo( "A.SimpleMethod" ) ); Assert.That( _lastCalledParam, Is.EqualTo( 2 ) ); @@ -138,23 +138,23 @@ public void NonVirtualInstanceInvoker() Type tB = typeof( B ); { // Null or MissingMethodException. - Func fUnk1 = tA.GetNonVirtualInvoker>( "SimpleMethod" ); + Func fUnk1 = DelegateHelper.GetNonVirtualInvoker>( tA, "SimpleMethod" ); Assert.That( fUnk1, Is.Null ); - Func fUnk2 = tA.GetNonVirtualInvoker>( "SimpleMethoddUnk" ); + Func fUnk2 = DelegateHelper.GetNonVirtualInvoker>( tA, "SimpleMethoddUnk" ); Assert.That( fUnk2, Is.Null ); - Assert.Throws( () => tA.GetNonVirtualInvoker>( "SimpleMethod", true ) ); - Assert.Throws( () => tA.GetNonVirtualInvoker>( "SimpleMethodUnk", true ) ); + Assert.Throws( () => DelegateHelper.GetNonVirtualInvoker>( tA, "SimpleMethod", true ) ); + Assert.Throws( () => DelegateHelper.GetNonVirtualInvoker>( tA, "SimpleMethodUnk", true ) ); - Assert.That( tA.GetNonVirtualInvoker>( "SimpleMethod", false ), Is.Null ); - Assert.That( tA.GetNonVirtualInvoker>( "SimpleMethodUnk", false ), Is.Null ); + Assert.That( DelegateHelper.GetNonVirtualInvoker>( tA, "SimpleMethod", false ), Is.Null ); + Assert.That( DelegateHelper.GetNonVirtualInvoker>( tA, "SimpleMethodUnk", false ), Is.Null ); } A a = new A(); B b = new B(); { - Func fA = tA.GetNonVirtualInvoker>( "SimpleMethod" ); + Func fA = DelegateHelper.GetNonVirtualInvoker>( tA, "SimpleMethod" ); Assert.That( fA( a, 20 ), Is.EqualTo( "20" ) ); Assert.That( _lastCalledName, Is.EqualTo( "A.SimpleMethod" ) ); Assert.That( _lastCalledParam, Is.EqualTo( 20 ) );