createDelegate方法,而不是用于思考的SetValue(CreateDelegate i

2019-09-24 07:45发布

我试图执行乔恩斯基特对解决这个问题,在此发布的博客文章来替代SetValue使用委托非反射法的方法。

从解决方案的差异博客文章是SetValuevoid ,而我得到的The type 'System.Void' may not be used as a type argument. 在该行例外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;
    }
}

Answer 1:

您正在使用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;
    }
}

当你不想打电话给像乔恩斯基特任意方法,则可以简化你的代码了很多 。 没有必要为一个呼叫MethodInfo.Invoke在你的代码,因为这是没有必要的代表。 你可以简单地调用SetValue对返回的直接PropertyInfo 。 有没有必要使用委托,反过来反正调用正是方法的弯路。 此外,如类型转换是不必要SetValue需要一个object反正。
你的代码可能是这么简单:

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;
    }
}

性能测试结果表明,该版本只需要大约50%的前一个。
在性能上一个微小的增加是由于这样的事实,我们避免了我们在电话链上的两个不必要的代表。 然而,绝大多数的速度的提高奠定了在我们删除了类型转换的事实。



文章来源: CreateDelegate instead of Reflection for SetValue