在我開始解釋代碼之前,我會首先給出我的用例,以便您能夠理解發生了什麼以及爲什麼發生。泛型,Lambda和反射問題[複雜]
先決條件:
- 讓有一個服務器(隊列調度器/緩衝在客戶端服務器術語)
- 讓有一個或多個管理客戶端(客戶端服務器術語監製)
- 讓有是客戶端(客戶端服務器術語消費者)
工作流程:
- 管理客戶端寫了一個C#腳本,向服務器發送
- 腳本得到由C#CodeDomProvider編譯
- 腳本可以給回一個CallQueue結果,或
- 如果發生服務器緩存CallQueue只執行服務器上的東西
- 在此期間其他管理客戶端可以發送被處理
- 一些客戶端連接到服務器並請求一個CallQueue
- CLIEN新腳本t獲取CallQueue,並在指定的時間執行它
一切都很好,直到這一點,並完美地工作。
現在的技術部分:
的CallQueue是使用lambda表達式作爲通用方法輸入一個類,並且執行在客戶端上的隊列中的呼叫所需存儲反射數據。
爲什麼所有這些複雜.. lambda泛型等?類型安全。 管理客戶端是愚蠢的,只需要知道一些寫腳本的方法,而不是真正的程序員。所以發送一個整數而不是一個字符串或命名一個打印錯誤的屬性可能會經常發生。這就是爲什麼腳本使用lambdas和泛型來限制某人可以輸入的內容。
這會在服務器上編譯並在錯誤的情況下被拒絕。
這是一個管理客戶端會寫一個象徵性的腳本:
CallQueue cc = new CallQueue(new DateTime(2012,12,21,10,0,0));
// set property Firstname to "test person"
cc.AddPropertySet<Person, string>(x => x.FirstName, "test person");
// call method ChangeDescription with parameter "test order"
cc.AddVoidMethodCall<Order, string>(x => x.ChangeDescription, "test order");
// call method Utility.CreateGuid and send result to Person.PersonId
cc.AddFunctionCallWithDestinationPropertySet<Utility, Guid, Person>(src => src.CreateGuid, dst => dst.PersonId);
什麼是客戶端將得到的是一個CallQueue實例並執行它像這樣:
Order order = new Order();
Person person = new Person();
Utility util = new Utility();
CallQueue cc = /* already got from server */;
// when you call this execute the call queue will do the work
// on object instances sent inside the execute method
cc.Execute(new List<object> { order, person, util });
了這裏的一切是罰款和類型安全,但有暗示:
- 客戶端完全知道哪些對象必須發送到e xecute方法,通過設計硬編碼
- 管理客戶端可以編寫腳本,就不會被髮送到,但仍然在服務器上編譯,因爲類型存在
就拿對象進行操作:
cc.AddFunctionCall<Int32, string>(x => x.ToString);
這將編譯,但在客戶端執行時會失敗,因爲它不會將Int32發送到execute方法。
好了,唧唧歪歪.... 所以,問題是:
如何限制一組允許的類型的通用方法 - 而不是通過定義繼承:
where T : something
但更像
where listOftypes.Contains(T)
或者說限制什麼可以進入任何的同類解決方案...... 我沒有找到這個通用contraint ...
這裏是CallQueue類:
[Serializable]
public class CallQueue : List<CallQueue.Call>
{
[Serializable]
public struct Call {
public MethodInfo Method;
public MethodInfo DestinationProperty;
public object[] Parameters;
public Call(MethodInfo m, MethodInfo d, object[] p) {
Method = m;
Parameters = p;
DestinationProperty = d;
}
}
public CallQueue(DateTime when) {
ScheduledTime = when;
}
public DateTime ScheduledTime
{
get;
set;
}
public void AddFunctionCall<TSrcClass, TResult>(Expression<Func<TSrcClass, Func<TResult>>> expr)
{
MethodResolver((LambdaExpression)expr, null, new object[] {});
}
public void AddFunctionCallWithDestinationPropertySet<TSrcClass, TResult, TDest>(Expression<Func<TSrcClass, Func<TResult>>> expr, Expression<Func<TDest, TResult>> dest)
{
MethodResolver((LambdaExpression)expr, dest, new object[] { });
}
public void AddFunctionCallWithDestinationPropertySet<TSrcClass, TParam1, TResult, TDest>(Expression<Func<TSrcClass, Func<TParam1, TResult>>> expr, TParam1 param, Expression<Func<TDest, TResult>> dest)
{
MethodResolver((LambdaExpression)expr, dest, new object[] { param });
}
public void AddFunctionCall<TSrcClass, TParam1, TResult>(Expression<Func<TSrcClass, Func<TParam1, TResult>>> expr, TParam1 param)
{
MethodResolver((LambdaExpression)expr, null, new object[] {param});
}
public void AddFunctionCallWithDestinationPropertySet<TSrcClass, TParam1, TParam2, TResult, TDest>(Expression<Func<TSrcClass, Func<TParam1, TParam2, TResult>>> expr, TParam1 param, TParam2 param2, Expression<Func<TDest, TResult>> dest)
{
MethodResolver((LambdaExpression)expr, dest, new object[] { param, param2 });
}
public void AddFunctionCall<TSrcClass, TParam1, TParam2, TResult>(Expression<Func<TSrcClass, Func<TParam1, TParam2, TResult>>> expr, TParam1 param, TParam2 param2)
{
MethodResolver((LambdaExpression)expr, null, new object[] { param, param2 });
}
public void AddVoidMethodCall<TSrcClass, TParam>(Expression<Func<TSrcClass, Action<TParam>>> expr, TParam param)
{
MethodResolver((LambdaExpression)expr, null, new object[] { param });
}
public void AddVoidMethodCall<TSrcClass, TParam1, TParam2>(Expression<Func<TSrcClass, Action<TParam1, TParam2>>> expr, TParam1 param, TParam2 param2)
{
MethodResolver((LambdaExpression)expr, null, new object[] { param, param2 });
}
public void AddVoidMethodCall<TSrcClass, TParam1, TParam2, TParam3>(Expression<Func<TSrcClass, Action<TParam1, TParam2, TParam3>>> expr, TParam1 param, TParam2 param2, TParam3 param3)
{
MethodResolver((LambdaExpression)expr, null, new object[] { param, param2, param3 });
}
public void AddPropertySet<TSrcClass, TParam1>(Expression<Func<TSrcClass, TParam1>> expr, TParam1 param)
{
PropertyResolver((LambdaExpression)expr, new object[] {param});
}
public void Execute(List<object> instances) {
foreach (var call in this) {
var owner = instances.Find(o => o.GetType() == call.Method.DeclaringType);
if (call.DestinationProperty != null)
{
// execute method get result and set to destination property
object res = call.Method.Invoke(owner, call.Parameters);
var destOwner = instances.Find(o => o.GetType() == call.DestinationProperty.DeclaringType);
call.DestinationProperty.Invoke(destOwner, new object[] {res});
}
else
{
// just execute method
call.Method.Invoke(owner, call.Parameters);
}
}
}
private void MethodResolver(LambdaExpression expr, LambdaExpression dest, object[] param)
{
var body = (UnaryExpression)expr.Body;
var methodCall = (MethodCallExpression)body.Operand;
var constant = (ConstantExpression)methodCall.Arguments[2];
var method = (MethodInfo)constant.Value;
MethodInfo dmethod = null;
if (dest != null)
{
var prop = (MemberExpression)dest.Body;
var propMember = (PropertyInfo)prop.Member;
dmethod = propMember.GetSetMethod();
}
this.Add(new Call(method, dmethod, param));
Console.WriteLine(method.Name);
}
private void PropertyResolver(LambdaExpression expr, object[] param)
{
var prop = (MemberExpression)expr.Body;
var propMember = (PropertyInfo)prop.Member;
var method = propMember.GetSetMethod();
this.Add(new Call(method, null, param));
Console.WriteLine(method.Name);
}
}
非常感謝你。 乾杯!
實際上界面的東西是可以工作的......當我想到這個時,我想......我不能將繼承和其他方法添加到類中,但現在我想到了它......我可以使用一個EMPTY接口?沒有繼承,沒有附加的方法...我必須先看看是否有部署的影響,但這可以工作... – 2011-04-29 01:46:56
是的,我可以走了,感謝提醒我再想想:) – 2011-04-30 14:40:38