我正在尝试使用我只在运行时知道的类型运行泛型方法。我可以使用反射进行此调用,但性能非常糟糕。我的研究表明,您可以使用反射创建委托,然后所有后续运行将更快。
我想将这些反映的代表保存到字典中。然后我的程序将检查方法是否已经定义,如果没有,那么它可以创建一个新的委托。
我的程序有两种主要方法。第一个接受一种类,它将保存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
}