如何保存和运行动态泛型方法

时间:2017-03-24 14:44:43

标签: c# generics dynamic reflection delegates

我正在尝试使用我只在运行时知道的类型运行泛型方法。我可以使用反射进行此调用,但性能非常糟糕。我的研究表明,您可以使用反射创建委托,然后所有后续运行将更快。

我想将这些反映的代表保存到字典中。然后我的程序将检查方法是否已经定义,如果没有,那么它可以创建一个新的委托。

我的程序有两种主要方法。第一个接受一种类,它将保存DataSet的结果。 DataSet有多个DataTable,需要绑定到属性。这些属性都是具有正确属性的对象列表,用于保存DataTable列。

我意识到即使我做这项工作也可能对于长期支持来说太过分裂,但现在我必须找到一个解答:)

public static class Common
{
    //http://blogs.msmvps.com/jonskeet/2008/08/09/making-reflection-fly-and-exploring-delegates/

    private static Dictionary<Type, Delegate> _toListCache = new Dictionary<Type, Delegate>();

    #region Data bind methods

    /// <summary>
    /// Extension Method to bind DataSet to model properties
    /// </summary>
    /// <typeparam name="T">Type of model</typeparam>
    public static T ToModel<T>(DataSet ds)
    {
        //for this to function we need multiple tables with the first table being the DataSet ID
        ////EXAMPLE return value:
        ////Index DataTableName
        ////0     DataSetInfo
        ////1     ContractInfo
        ////2     InvoiceInfo
        if (ds.Tables.Count < 2) throw new DataException("Invalide DataSet format");

        var ob = Activator.CreateInstance<T>();
        PropertyInfo[] properties = typeof(T).GetProperties();

        DataTable setIds = ds.Tables[0];
        foreach (DataRow table in setIds.Rows)
        {
            //skip the first ID table
            if (table["DataTableName"].ToString() == "DataSetInfo") continue;

            int tableIndex = (int)table["Index"];
            string tableName = table["DataTableName"].ToString();

            //check if property exists matching the table name
            PropertyInfo property = ob.GetType().GetProperty(tableName);
            if (property != null)
            {
                //get list type
                Type propertyType = property.GetType();
                if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>))
                {
                    //Get type T out of List<T>
                    Type listType = propertyType.GetGenericArguments()[0];
                    DataTable dt = ds.Tables[tableIndex];

                    //call ToList with a dynamic type
                    if (_toListCache.ContainsKey(listType))
                    {
                        //delegate has already been defined
                        var list = _toListCache[listType].DynamicInvoke(dt);
                        property.SetValue(ob, list);
                    }
                    else
                    {
                        MethodInfo createDelegate = typeof(Common).GetMethod("AddDelegateToCache").MakeGenericMethod(listType);
                        var func = createDelegate.Invoke(null, new object[] { "ToList" });

                        var list = _toListCache[listType](dt);
                        property.SetValue(ob, list);
                    }
                }
                else
                    throw new TypeLoadException(string.Format("Property {0} is not a type of List<T>", tableName));
            }
            else
                throw new TypeLoadException(string.Format("Property {0} does not exist.", tableName));
        }

        return ob;
    }

    /// <summary>
    /// Extension Method to bind DataTable to model properties
    /// </summary>
    /// <typeparam name="T">Type of model</typeparam>
    public static List<T> ToList<T>(DataTable dt)
    {
        List<string> columns = (from DataColumn dc in dt.Columns select dc.ColumnName).ToList();

        FieldInfo[] fields = typeof(T).GetFields();
        PropertyInfo[] properties = typeof(T).GetProperties();
        List<T> lst = new List<T>();

        foreach (DataRow dr in dt.Rows)
        {
            var ob = Activator.CreateInstance<T>();

            ////for performance we will only look at properties
            ////sets field values that match a column in the DataTable
            //foreach (var fieldInfo in fields.Where(fieldInfo => columns.Contains(fieldInfo.Name)))
            //    fieldInfo.SetValue(ob, !dr.IsNull(fieldInfo.Name) ? dr[fieldInfo.Name] : fieldInfo.FieldType.IsValueType ? Activator.CreateInstance(fieldInfo.FieldType) : null);

            //sets property values that match a column in the DataTable
            foreach (var propertyInfo in properties.Where(propertyInfo => columns.Contains(propertyInfo.Name)))
                propertyInfo.SetValue(ob, !dr.IsNull(propertyInfo.Name) ? dr[propertyInfo.Name] : propertyInfo.PropertyType.IsValueType ? Activator.CreateInstance(propertyInfo.PropertyType) : null);
            lst.Add(ob);
        }

        return lst;
    }

    #endregion

    #region Generic delegate methods

    public static Func<T, object, object> AddDelegateToCache<T>(string method) where T : class
    {
        MethodInfo genericMethod = typeof(Common).GetMethod("GenericMethod", new Type[] { typeof(T) });
        Func<T, object, object> genericMethodFunc = GenericMethod<T>(genericMethod);
        _toListCache.Add(typeof(T), genericMethodFunc);
        return genericMethodFunc;
    }

    /// <summary>
    /// Runs a generic method with a dynamic type
    /// </summary>
    /// <typeparam name="T">Return type</typeparam>
    /// <param name="method">Target generic method</param>
    static Func<T, object, object> GenericMethod<T>(MethodInfo method) where T : class
    {
        // First fetch the generic form
        MethodInfo genericHelper = typeof(Common).GetMethod("GenericMethodHelper", BindingFlags.Static | BindingFlags.NonPublic);

        // Now supply the type arguments
        MethodInfo constructedHelper = genericHelper.MakeGenericMethod
            (typeof(T), method.GetParameters()[0].ParameterType, method.ReturnType);

        // Now call it. The null argument is because it’s a static method.
        object ret = constructedHelper.Invoke(null, new object[] { method });

        // Cast the result to the right kind of delegate and return it
        return (Func<T, object, object>)ret;
    }

    /// <summary>
    /// used to cast object to TParam and TReturn
    /// This allows fast dynamic generic methods to run
    /// </summary>
    static Func<TTarget, object, object> GenericMethodHelper<TTarget, TParam, TReturn>(MethodInfo method)
        where TTarget : class
    {
        // Convert the slow MethodInfo into a fast, strongly typed, open delegate
        Func<TTarget, TParam, TReturn> func = (Func<TTarget, TParam, TReturn>)Delegate.CreateDelegate
            (typeof(Func<TTarget, TParam, TReturn>), method);

        // Now create a more weakly typed delegate which will call the strongly typed one
        Func<TTarget, object, object> ret = (TTarget target, object param) => func(target, (TParam)param);
        return ret;
    }

    #endregion
}

0 个答案:

没有答案