设我们有这么一个类:
public class Test
{
public int id { get; private set;}
}
那么当我们要在类型的外部修改id值的时候,该怎么办呢?当然是把set前面的private去掉,本文终。某些时候,我们因为限制而无法修改类型的内容(例如使用第三方库,再例如为了保证编程规范), 那么我们就需要这么写:
//For test
Test a = new Test ();
Test b = new Test ();
Type type = typeof(Test);
PropertyInfo pi = type.GetProperty ("id");
pi.SetValue (a, -1, null);
pi.SetValue (b, -2, null);
第一个参数是该类型(Test)的对象(a或b)。
第二个参数就是我们将要设置的值,类型是object。
这TM就很尴尬了,我们都知道,int类型转object会进行装箱操作,而真正赋值的时候又会进行拆箱操作,一大波GC正在袭来。
而且这个方法慢,非常慢,出了名的慢,因为在真正赋值之前,会进行一系列的类型判断。
那么,如何解决这些问题?我们就需要使用黑科技——Delegate.CreateDelegate,代码很简单:
var act = Delegate.CreateDelegate (typeof(Action), pi.GetSetMethod (true)) as Action;
act (a, 1);
act (b, 2);
顺手贴上代码:
public static Func BuildGetAccessor(Expression> propertySelector)
{
return propertySelector.GetPropertyInfo().GetGetMethod().CreateDelegate>();
}
public static Action BuildSetAccessor(Expression> propertySelector)
{
return propertySelector.GetPropertyInfo().GetSetMethod().CreateDelegate>();
}
// a generic extension for CreateDelegate
public static T CreateDelegate(this MethodInfo method) where T : class
{
return Delegate.CreateDelegate(typeof(T), method) as T;
}
public static PropertyInfo GetPropertyInfo(this Expression> propertySelector)
{
var body = propertySelector.Body as MemberExpression;
if (body == null)
throw new MissingMemberException("something went wrong");
return body.Member as PropertyInfo;
}
public class Accessor
{
public static Accessor Create(Expression> memberSelector)
{
return new GetterSetter(memberSelector);
}
public Accessor Get(Expression> memberSelector)
{
return Create(memberSelector);
}
public Accessor()
{
}
class GetterSetter : Accessor
{
public GetterSetter(Expression> memberSelector) : base(memberSelector)
{
}
}
}
public class Accessor : Accessor
{
Func Getter;
Action Setter;
public bool IsReadable { get; private set; }
public bool IsWritable { get; private set; }
public T this[S instance]
{
get
{
if (!IsReadable)
throw new ArgumentException("Property get method not found.");
return Getter(instance);
}
set
{
if (!IsWritable)
throw new ArgumentException("Property set method not found.");
Setter(instance, value);
}
}
protected Accessor(Expression> memberSelector) //access not given to outside world
{
var prop = memberSelector.GetPropertyInfo();
IsReadable = prop.CanRead;
IsWritable = prop.CanWrite;
AssignDelegate(IsReadable, ref Getter, prop.GetGetMethod());
AssignDelegate(IsWritable, ref Setter, prop.GetSetMethod());
}
void AssignDelegate(bool assignable, ref K assignee, MethodInfo assignor) where K : class
{
if (assignable)
assignee = assignor.CreateDelegate();
}
}
如果我们只能得到类型的Type,并不能直接使用类型,那该怎么办呢?
(我已经得到了一个不算很成熟的解决方案,有缘我再拿出来讨论吧。)