在我开始解释代码之前,我会首先给出我的用例,以便您能够理解发生了什么以及为什么发生。泛型,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