1.引言
Scheduled Timer的由Timer定时器触发执行任务,Timer每隔一段时间(短时间)触发Elapsed事件,大多数做法是给定时器Timer的Elapsed事件赋值,Scheduled Timer的做法差不多,但又有不同。Scheduled Timer做法是把定时器的Elapsed私有化了,给了它一个固定私有的方法Timer_Elapsed,在Timer_Elapsed里有多个Job,每个Job才是对应我们的任务方法。里我们并不是在调用时给Elapsed赋值,Elapsed事件只是一个入口,在Elapsed事件里进行执行我们的任务方法,这个任务方法可以是一个,也可以是集合,这个方法里面的参数也是动态的,可以是外部传入,也可以说是线程里执行时传入。这节介绍任务方法。
2.任务方法
Job下的MethodCall是我们的任务方法,大家肯定想到了委托,对的,Scheduled Timer也是使用委托,因为它可以赋值一个方法签名。有了方法,另一个就是方法参数,参数值,可以初始化时传入,可以Timer执行时动态创建参数或者修改,这些委托都可以办到。先来看看委托Delegate的DynamicInvoke声明,Scheduled Timer就用到它。
[Serializable] [ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public abstract class Delegate : ICloneable, ISerializable { // // 摘要: // 动态调用(后期绑定)由当前委托所表示的方法。 // // 参数: // args: // 作为参数传递给当前委托所表示的方法的对象数组。- 或 -如果当前委托所表示的方法不需要参数,则为 null。 // // 返回结果: // 委托所表示的方法返回的对象。 // // 异常: // System.MemberAccessException: // 调用方不能访问委托所表示的方法(例如,当该方法为私有时)。- 或 -args 中列出的参数的数目、顺序或类型无效。 // // System.Reflection.TargetException: // 委托所表示的方法是实例方法,目标对象为 null。- 或 -对对象或类调用委托所表示的方法,但该对象或类不支持这种方法。 // // System.Reflection.TargetInvocationException: // 封装的方法之一引发异常。 [SecuritySafeCritical] public object DynamicInvoke(params object[] args); }
参数args不只是我们初始化时传入,而且还可以在运行中动态添加、修改参数。于是我们要定义一个自己的参数类,方便操作。
/// <summary> /// 参数设置器 /// </summary> public interface IParameterSetter { /// <summary> /// 设置获取第N个参数 /// </summary> void reset(); /// <summary> /// 获取参数值 /// </summary> /// <param name="pi"></param> /// <param name="ParameterLoc">第几个参数</param> /// <param name="parameter">参数值</param> /// <returns>是否成功获取</returns> bool GetParameterValue(ParameterInfo pi, int ParameterLoc, ref object parameter); }
IParameterSetter接口做两件事:
- reset() 设置获取参数的起始值,一般设为0
- GetParameterValue 获取第N个参数的值
接下来简单实现一个IParameterSetter如下:
/// <summary> /// 动态参数类 /// </summary> public class OrderParameterSetter : IParameterSetter { object[] _paramList; int _counter; public OrderParameterSetter(params object[] _params) { _paramList = _params; } public void reset() { _counter = 0; } public bool GetParameterValue(ParameterInfo pi, int parameterLoc, ref object parameter) { if (_counter >= _paramList.Length) return false; parameter = _paramList[_counter++]; return true; } }
在OrderParameterSetter中看到构造函数我们知道,这个参数类可以动态构造多个值,譬如一个方法有10个参数,你可以使用OrderParameterSetter来初始化前面5个,后面运行时的时候再初始化后面5个。由此就引出一个问题,我们的IParameterSetter 不是一个,后面再动态创建时会有多个,于是,我们要在任务方法里创建一个IParameterSetter的集合,Scheduled Timer为了方便管理,自定义了一个类来管理。
/// <summary> ///参数类收集器 /// </summary> public class ParameterSetterList { List<IParameterSetter> _List = new List<IParameterSetter>(); public void Add(IParameterSetter setter) { _List.Add(setter); } public IParameterSetter[] ToArray() { return _List.ToArray(); } public void reset() { foreach (IParameterSetter Setter in _List) Setter.reset(); } public object[] GetParameters(MethodInfo Method) { ParameterInfo[] Params = Method.GetParameters(); object[] Values = new object[Params.Length]; //TODO: Update to iterate backwards for (int i = 0; i < Params.Length; ++i) SetValue(Params[i], i, ref Values[i]); return Values; } public object[] GetParameters(MethodInfo Method, IParameterSetter LastSetter) { ParameterInfo[] Params = Method.GetParameters(); object[] Values = new object[Params.Length]; //TODO: Update to iterate backwards for (int i = 0; i < Params.Length; ++i) { if (!SetValue(Params[i], i, ref Values[i])) LastSetter.GetParameterValue(Params[i], i, ref Values[i]); } return Values; } bool SetValue(ParameterInfo Info, int i, ref object Value) { foreach (IParameterSetter Setter in _List) { if (Setter.GetParameterValue(Info, i, ref Value)) return true; } return false; } }
ParameterSetterList类比较简单,就是管理多个IParameterSetter。
参数建好接下来就是我们的方法了,Scheduled Timer里面是用继承来实现的,而非组合,这样可以简化方法类,先看基类。
public class MethodCallBase { ParameterSetterList _paramList = new ParameterSetterList(); public ParameterSetterList ParamList { get { return _paramList; } } protected object[] GetParameterList(MethodInfo method) { ParamList.reset(); object[] Params = ParamList.GetParameters(method); return Params; } protected object[] GetParameterList(MethodInfo method, IParameterSetter _params) { ParamList.reset(); object[] objParams = ParamList.GetParameters(method, _params); return objParams; } }
接下来就继承MethodCallBase,Scheduled Timer 里有多个版本实现,这里介绍DelegateMethodCall
/// <summary> ///方法执行的接口 /// </summary> public interface IMethodCall { ParameterSetterList ParamList { get; } object Execute(); object Execute(IParameterSetter Params); void EventHandler(object obj, EventArgs e); IAsyncResult BeginExecute(AsyncCallback callback, object obj); IAsyncResult BeginExecute(IParameterSetter Params, AsyncCallback callback, object obj); } public class DelegateMethodCall : MethodCallBase, IMethodCall { Delegate _f; public DelegateMethodCall(Delegate f) { _f = f; } public DelegateMethodCall(Delegate f, params object[] Params) { if (f.Method.GetParameters().Length < Params.Length) throw new ArgumentException("Too many parameters specified for delegate", "f"); _f = f; ParamList.Add(new OrderParameterSetter(Params)); } public DelegateMethodCall(Delegate f, IParameterSetter Params) { _f = f; ParamList.Add(Params); } public object Execute() { return _f.DynamicInvoke(GetParameterList(_f.Method)); } public object Execute(IParameterSetter Params) { return _f.DynamicInvoke(GetParameterList(_f.Method, Params)); } }
IMethodCall接口来约束,提供了 异步调用,同步调用多个版本,DelegateMethodCall 是其中的一个实现。
调用也很简单
Action<string> delM=o => { Console.WriteLine(o); }; IMethodCall method = new DelegateMethodCall(delM, "hello method");
method.Execute();
3.总结
动态调用方法,都是利用委托,为了方便使用,参数类,方法Scheduled Timer都进行了封装,有好几个版本,如动态参数,键值对等等,自己也可以参照约定,自定义实现。
作者:Qlin
出处:http://www.cnblogs.com/qqlin/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。