C# CreateDelegate代替SetValue的反射

C# CreateDelegate代替SetValue的反射,c#,reflection,delegates,C#,Reflection,Delegates,我试着实现Jon Skeet的解决方案,用一个使用委托的非反射方法替换SetValue方法 与中的解决方案不同的是,SetValue是void,我得到的类型“System.void”不能用作类型参数。行MethodInfo-miConstructedHelper=miGenericHelper.MakeGenericMethod(typeof(G),pMethod.GetParameters()[0]。ParameterType,pMethod.ReturnType)处的异常; 下面是我对Mag

我试着实现Jon Skeet的解决方案,用一个使用委托的非反射方法替换
SetValue
方法

与中的解决方案不同的是,
SetValue
void
,我得到的
类型“System.void”不能用作类型参数。
MethodInfo-miConstructedHelper=miGenericHelper.MakeGenericMethod(typeof(G),pMethod.GetParameters()[0]。ParameterType,pMethod.ReturnType)处的异常;

下面是我对
MagicMethod
的实现:

public class Instantiator<T> where T : new()
{
    private T instance;
    private IDictionary<string, PropertyInfo> properties;

    private Func<PropertyInfo, object, object> _fncSetValue;

    public Instantiator()
    {
        Type type = typeof(T);
        properties = type.GetProperties().GroupBy(p => p.Name).ToDictionary(g => g.Key, g => g.ToList().First());

        MethodInfo miSetValue = typeof(PropertyInfo).GetMethod("SetValue", new Type[] { typeof(object), typeof(object), typeof(object[]) });
        _fncSetValue = SetValueMethod<PropertyInfo>(miSetValue);
    }

    public void CreateNewInstance()
    {
        instance = new T();
    }

    public void SetValue(string pPropertyName, object pValue)
    {
        if (pPropertyName == null) return;
        PropertyInfo property;
        if (!properties.TryGetValue(pPropertyName, out property)) return;
        TypeConverter tc = TypeDescriptor.GetConverter(property.PropertyType);

        //substitute this line
        //property.SetValue(instance, tc.ConvertTo(pValue, property.PropertyType), null);
        //with this line
        _fncSetValue(property, new object[] { instance, tc.ConvertTo(pValue, property.PropertyType), null });
    }

    public T GetInstance()
    {
        return instance;
    }

    private static Func<G, object, object> SetValueMethod<G>(MethodInfo pMethod) where G : class
    {
        MethodInfo miGenericHelper = typeof(Instantiator<T>).GetMethod("SetValueMethodHelper", BindingFlags.Static | BindingFlags.NonPublic);
        MethodInfo miConstructedHelper = miGenericHelper.MakeGenericMethod(typeof(G), pMethod.GetParameters()[0].ParameterType, pMethod.ReturnType);
        object retVal = miConstructedHelper.Invoke(null, new object[] { pMethod });
        return (Func<G, object, object>) retVal;
    }

    private static Func<TTarget, object, object> SetValueMethodHelper<TTarget, TParam, TReturn>(MethodInfo pMethod) where TTarget : class
    {
        Func<TTarget, TParam, TReturn> func = (Func<TTarget, TParam, TReturn>)Delegate.CreateDelegate(typeof(Func<TTarget, TParam, TReturn>), pMethod);
        Func<TTarget, object, object> retVal = (TTarget target, object param) => func(target, (TParam) param);
        return retVal;
    }
}
公共类实例化器,其中T:new()
{
私人T实例;
私人词典财产;
私有函数fncSetValue;
公共实例化器()
{
类型=类型(T);
properties=type.GetProperties().GroupBy(p=>p.Name).ToDictionary(g=>g.Key,g=>g.ToList().First());
MethodInfo miSetValue=typeof(PropertyInfo).GetMethod(“SetValue”,新类型[]{typeof(object),typeof(object),typeof(object[])});
_fncSetValue=SetValueMethod(miSetValue);
}
public void CreateNewInstance()
{
实例=新的T();
}
public void SetValue(字符串PPPropertyName,对象pValue)
{
if(ppPropertyName==null)返回;
财产信息财产;
如果(!properties.TryGetValue(ppPropertyName,out属性))返回;
TypeConverter tc=TypeDescriptor.GetConverter(property.PropertyType);
//替换这一行
//SetValue(实例,tc.ConvertTo(pValue,property.PropertyType),null);
//用这条线
_fncSetValue(属性,新对象[]{instance,tc.ConvertTo(pValue,property.PropertyType),null});
}
公共T GetInstance()
{
返回实例;
}
私有静态Func SetValueMethod(MethodInfo-pMethod),其中G:class
{
MethodInfo miGenericHelper=typeof(实例化器).GetMethod(“SetValueMethodHelper”,BindingFlags.Static | BindingFlags.NonPublic);
MethodInfo miConstructedHelper=migenericelper.MakeGenericMethod(typeof(G),pMethod.GetParameters()[0]。ParameterType,pMethod.ReturnType);
object retVal=miConstructedHelper.Invoke(null,新对象[]{pMethod});
返回(Func)返回;
}
私有静态Func SetValueMethodHelper(MethodInfo-pMethod),其中TTarget:class
{
Func Func=(Func)Delegate.CreateDelegate(typeof(Func),pMethod);
Func retVal=(t目标,对象参数)=>Func(目标,(TParam)参数);
返回返回;
}
}

您正在代码中使用
Func
Func
用于具有返回类型的方法。对于返回
void
的方法,需要使用
Action


您的代码需要如下所示:

public class Instantiator<T> where T : new()
{
    private T instance;
    private IDictionary<string, PropertyInfo> properties;

    private Action<PropertyInfo, object, object, object> _fncSetValue;

    public Instantiator()
    {
        Type type = typeof(T);
        properties = type.GetProperties()
                         .GroupBy(p => p.Name)
                         .ToDictionary(g => g.Key, g => g.ToList().First());

        var types = new Type[] { typeof(object), typeof(object),
                                 typeof(object[]) };
        var miSetValue = typeof(PropertyInfo).GetMethod("SetValue", types);
        _fncSetValue = SetValueMethod<PropertyInfo>(miSetValue);
    }

    public void CreateNewInstance()
    {
        instance = new T();
    }

    public void SetValue(string pPropertyName, object pValue)
    {
        if (pPropertyName == null) return;
        PropertyInfo property;
        if (!properties.TryGetValue(pPropertyName, out property)) return;
        TypeConverter tc = TypeDescriptor.GetConverter(property.PropertyType);

        var value = tc.ConvertTo(pValue, property.PropertyType);
        _fncSetValue(property, instance, value, null);
    }

    public T GetInstance()
    {
        return instance;
    }

    private static Action<G, object, object, object> SetValueMethod<G>(MethodInfo pMethod) where G : class
    {
        var miGenericHelper = 
            typeof(Instantiator<T>).GetMethod("SetValueMethodHelper", 
                                              BindingFlags.Static | 
                                              BindingFlags.NonPublic);

        var parameters = pMethod.GetParameters();
        var miConstructedHelper = miGenericHelper.MakeGenericMethod(typeof(G), 
                                      parameters[0].ParameterType,
                                      parameters[1].ParameterType,
                                      parameters[2].ParameterType);

        var retVal = miConstructedHelper.Invoke(null, new object[] { pMethod });
        return (Action<G, object, object, object>) retVal;
    }

    private static Action<TTarget, object, object, object> SetValueMethodHelper<TTarget, TParam1, TParam2, TParam3>(MethodInfo pMethod) where TTarget : class
    {
        var func = (Action<TTarget, TParam1, TParam2, TParam3>)Delegate.CreateDelegate(typeof(Action<TTarget, TParam1, TParam2, TParam3>), pMethod);
        Action<TTarget, object, object, object> retVal =
            (target, param1, param2, param3) => 
                func(target, (TParam1) param1, (TParam2) param2, (TParam3) param3);

        return retVal;
    }
}
性能测试表明,此版本仅占前一版本的50%左右。

性能的微小提高是由于我们在调用链中避免了两个不必要的委托。然而,速度提高的绝大多数原因在于我们删除了类型转换。

谢谢,这解决了上述异常。但是现在我得到了绑定到目标方法的
错误。
MethodInfo-miConstructedHelper=miGenericHelper.MakeGenericMethod(typeof(G),pMethod.GetParameters()[0].ParameterType,pMethod.ReturnType)行上
我认为这是因为
SetValue
有多个参数…@Mentoliptus:您需要删除
pMethod.ReturnType
。您需要从
SetValueMethodHelper
中删除泛型参数
TReturn
MakeGenericMethod
接受类型[]的参数,一个是目标,另一个是方法参数。博客文章中的方法有一个参数,因此是
pMethod.GetParameters()[0]。ParameterType
,但我有3个参数。哪里有三个参数?我添加了
,pMethod.GetParameters()[1]。ParameterType,pMethod.GetParameters()[2].ParameterType
但我不认为我必须将参数添加到所有
操作
声明中。非常感谢!我在这里学到了很多。
public class SimpleInstantiator<T> where T : new()
{
    private T instance;
    private IDictionary<string, PropertyInfo> properties;

    public SimpleInstantiator()
    {
        Type type = typeof(T);
        properties = type.GetProperties()
                         .GroupBy(p => p.Name)
                         .ToDictionary(g => g.Key, g => g.ToList().First());
    }

    public void CreateNewInstance()
    {
        instance = new T();
    }

    public void SetValue(string pPropertyName, object pValue)
    {
        if (pPropertyName == null) return;

        PropertyInfo property;
        if (!properties.TryGetValue(pPropertyName, out property)) return;

        property.SetValue(instance, pValue, null);
    }

    public T GetInstance()
    {
        return instance;
    }
}