適用情況:
為一個不確定的對象動態地為某一個未知的Property或多個 Property 賦值和取值
亮點:
非 Property.GetValue或 Property.SetValue ,使用委托代理緩存機制。
因此可以這樣用:
//假設是一個一個Entity對象
var instance = new Topic();
//得到其Property Dictionary
var propDic = new InstancePropertyDictionary(instance);
//無需轉換為Object地賦值
propDic.SetValue("屬性名稱",Int32值或Stirng值...);
//不存在類型轉換地取值
Int32 int32Value = propDic.GetInt32("屬性名稱");
string stringValue = propDic.GetString("屬性名稱");
以下是全部實現的代碼,單類,可直接使用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace Lx
{
/// <summary>
/// Instance的屬性高速讀/寫(無需轉換類型)字典
/// </summary>
class InstancePropertyDictionary
{
/// <summary>
/// Get委托
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <returns></returns>
delegate TResult Get<TResult>();
/// <summary>
/// Set委托
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <param name="value"></param>
delegate void Set<TValue>(TValue value);
/// <summary>
/// Instance
/// </summary>
object Target;
/// <summary>
/// Instance類型
/// </summary>
Type TargetType;
public InstancePropertyDictionary(object instance)
{
this.Target = instance;
this.TargetType = Target.GetType();
}
#region Set委托
/// <summary>
/// Key是屬性的名字
/// Value是強類型的委托
/// </summary>
Dictionary<string, Set<Int32>> setInt32Dic = new Dictionary<string, Set<Int32>>();
Dictionary<string, Set<string>> setStringDic = new Dictionary<string, Set<string>>();
#endregion
#region Get委托
Dictionary<string, Get<Int32>> getInt32Dic = new Dictionary<string, Get<Int32>>();
Dictionary<string, Get<string>> getStringDic = new Dictionary<string, Get<string>>();
#endregion
/// <summary>
/// 裝載一個類的屬性
/// </summary>
public void LoadProperty(params string[] names)
{
var props = TargetType.GetProperties();
foreach (var name in names)
{
foreach (var prop in props)
{
if (prop.Name == name)
{
CreateGetSet(prop);
}
}
}
}
/// <summary>
/// 創建屬性的Getter/Setter委托
/// </summary>
/// <param name="property"></param>
void CreateGetSet(PropertyInfo property)
{
string propName = property.Name;
var propType = property.PropertyType;
var propSetMethod = property.GetSetMethod();
var propGetMethod = property.GetGetMethod();
if (typeof(Int32) == propType)
{
var set = CreateSet<Int32>(propSetMethod);
setInt32Dic.Add(propName, set);
var get = CreateGet<Int32>(propGetMethod);
getInt32Dic.Add(propName, get);
}
else if (typeof(string) == propType)
{
var set = CreateSet<string>(propSetMethod);
setStringDic.Add(propName, set);
var get = CreateGet<string>(propGetMethod);
getStringDic.Add(propName, get);
}
//剩下的else if請自己實現
}
Set<T> CreateSet<T>(MethodInfo methodInfo)
{
var result = (Set<T>)Delegate.CreateDelegate(typeof(Set<T>), Target, methodInfo);
return result;
}
Get<T> CreateGet<T>(MethodInfo methodInfo)
{
var result = (Get<T>)Delegate.CreateDelegate(typeof(Get<T>), Target, methodInfo);
return result;
}
/// <summary>
/// Set值
/// </summary>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public void SetValue(string propertyName, Int32 value)
{
//去字典取得強類委托型
var dg = setInt32Dic[propertyName];
dg.Invoke(value);
}
public void SetValue(string propertyName, string value)
{
var dg = setStringDic[propertyName];
dg.Invoke(value);
}
/// <summary>
/// Get值
/// </summary>
/// <param name="propertyName"></param>
/// <returns></returns>
public Int32 GetInt32(string propertyName)
{
var dg = getInt32Dic[propertyName];
return dg.Invoke();
}
public string GetString(string propertyName)
{
var dg = getStringDic[propertyName];
return dg.Invoke();
}
}
}
我不知道Expression Tree是怎么使用的,是否比創建代理委托性能更好,所以貼出來,歡迎跟帖討論。
附:
.NET中 Delegate.CreateDelegate方法的實現
[SecuritySafeCritical]
public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (method == null)
{
throw new ArgumentNullException("method");
}
if (!(type is RuntimeType))
{
throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"), "type");
}
RuntimeMethodInfo info = method as RuntimeMethodInfo;
if (info == null)
{
throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeMethodInfo"), "method");
}
Type baseType = type.BaseType;
if ((baseType == null) || (baseType != typeof(MulticastDelegate)))
{
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeDelegate"), "type");
}
Delegate delegate2 = InternalAlloc(type.TypeHandle.GetRuntimeType());
if (delegate2.BindToMethodInfo(firstArgument, info.MethodHandle.GetMethodInfo(), info.GetDeclaringTypeInternal().TypeHandle.GetRuntimeType(), DelegateBindingFlags.RelaxedSignature))
{
return delegate2;
}
if (throwOnBindFailure)
{
throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTargMeth"));
}
return null;
}
我覺得這個類中,影響性能的關鍵就在於 Delegate.CreateDelegate 方法的具體是怎么做的,難道還是Methodinfo.Invoke(object, object[])嗎?
用Reflecter反射發現BindToMethodInfo被標記為 extern了。
關於反對本文所說的“非反射”的:
有人回復說,還是用到反射了。
我這里是說 不采用反射的方式去“動態Property賦值、取值”,本文標題也沒有誤導吧?
從
CreateSet<T>
CreateGet<T>
這兩個方法的實現可以知道,已經不是在用反射了,而是委托
前面用到了反射的地方,只是去取得Property的GetMethod和SetMethod,然后用來創建強類型的委托。
所以在給Property賦值的時候,是不存在反射的。
而且可以看 Secmoo回復 的測試結果,如果在取值、賦值過程中涉及到了反射機制,是決不會有能超過Expression的性能的。
