先看委托。 然后事件 当然可耻的直接用了书的源码。
首先.. 委托就和js中得回调一样,别人都用c举例...本人只能用js举例。。。。 ╮(╯▽╰)╭
function a(func)
{
var el= document.body;
func(el)
}
function b(el) { do something.. }
a(b);
委托也是这样.. 不过要麻烦很多.
internal delegate void Feedback(Int32 value); 先定义
public static void Main()
{
Counter(1, 3, new Feedback(FeedbackToConsole)); //这是调用... 同样是将方法传入
//Counter(1, 3, Program.FeedbackToConsole); //这样也是可以的
}
private static void Counter(Int32 from, Int32 to, Feedback fb)
{
for (Int32 val = from; val <= to; val++)
{
if (fb != null)
fb(val);
}
}
private static void FeedbackToConsole(Int32 value)
{
Console.WriteLine("Item=" + value);
}
关于协变 逆变
协变 就是委托返回的类型是派生类
逆变 就是返回基类..
当然 他们只能针对值类型
internal delegate object Feedback(Int32 value);
你绑定的方法可以是 string aaa(int i);
不过关于协变逆变的性能以后再讨论
前面调用的都是静态方法,这里传了一个实例,当然只是把栈中得地址传了过去
private static void InstanceDelegateDemo()
{
Program p = new Program();
Counter(1, 3, new Feedback(p.FeedbackToFile)); //这里传递的是引用..
Console.WriteLine();
}
关于写法. 委托从1.0到现在写法有一些变化..
internal delegate void Feedback(Int32 value);
public static void Main()
{
Counter(1, 3, new Feedback(FeedbackToMsgBox)); 1.0
Counter(1, 3, delegate(int a) 2.0 { //匿名方法
//你不必单独创建方法 就像上面的FeedbackToMsgBox,当然这玩意儿得建立在你只在这里使用
//感觉上就像闭包. 当然前提是他得引用上下文..
//当然简便代码是很重要的,很多时候调用方法需要传入一个委托, 没有他你就麻烦了。。
//也有缺点。 毕竟像过多外部参数 维护起来 啊哈 });
Counter(1,3, a => a++); 3.5
//貌似生成的代码是一个匿名方法 摊手..
//适当的使用委托(包括 匿名方法,lamb), 能让代码更优美~~~~~~~~~
}
private static void Counter(Int32 from, Int32 to, Feedback fb){}
关于他里面干了什么?
首先看IL.. 虽然看的恼火
internal delegate void Feedback(Int32 value);
.class auto ansi sealed nested assembly Feedback
extends [mscorlib]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}
.method public hidebysig newslot virtual instance class [mscorlib]System.IAsyncResult BeginInvoke(int32 'value', class [mscorlib]System.AsyncCallback callback, object 'object') runtime managed
{
}
.method public hidebysig newslot virtual instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
{
}
.method public hidebysig newslot virtual instance void Invoke(int32 'value') runtime managed
{
}
}
继承自 System.MulticastDelegate
第一个是一个构造
所有委托都有构造器,参数一个是 object 一个是int
开始我以为是传得参数,毕竟委托的参数也是int,后来重新写了一个 string参数的委托,发现构造的参数还是int。 忧郁。
Object是引用对象(引发装箱拆箱?),如果传入的是静态方法 Object 会是null
Int 据书上说是 标识了方法的一个特殊值...
然后 在构造器内部 object 和 Int 分别保存在_target _methouPtr
然后一个和原型一样的方法
这玩意就是调用原方法的方法.. 委托都会隐式的调用他.
然后就是三个变量
之前上面提到了
_target //如果你传入的不是静态方法, 那这个就存放你传进来的方法所属的类
_methouPtr //只是一个整数值,标示回调的方法
_invocationList //数组. 通常是null 只有在委托链的时候
关于委托链
Console.WriteLine("----- Chain Delegate Demo 1 -----");
Feedback fb1 = new Feedback(FeedbackToConsole);
Feedback fb2 = new Feedback(FeedbackToMsgBox);
Feedback fb3 = new Feedback(p.FeedbackToFile);
Feedback fbChain = null;
fbChain = (Feedback)Delegate.Combine(fbChain, fb1);
fbChain = (Feedback)Delegate.Combine(fbChain, fb2);
fbChain = (Feedback)Delegate.Combine(fbChain, fb3);
Counter(1, 2, fbChain);
Console.WriteLine();
fbChain = (Feedback)Delegate.Remove(fbChain, new Feedback(FeedbackToMsgBox));
Counter(1, 2, fbChain);
书上的例子很明确了
在添加的时候
_invocationList [0,1,2] 分别代表的3个委托,所以在 Counter执行的时候 会分别调用那3个委托
同样 也提供了Remove 删除不用的
当然也有简便方法
fbChain += fb1;
fbChain += fb2;
fbChain += fb3;
fbChain.GetInvocationList(); 返回Delegate[] over~
泛型委托
internal delegate void Feedback(T value); //只是一个T~ 其他不变
关于泛型这里就不赘述了 会在之后的写 到时候再反过来加链接吧
ok 谈谈委托的使用
其实都知道 委托在很多程度上可以用 接口来代替, 只是每次都弄个类 搞个接口 确实比较繁琐
委托给人的感觉就是不知道使用者要干什么,如果上下文清楚 个人感觉使用接口要好一点(当然,这对我来说只是规范问题)
其实这里还差一段代码... 就是测试 委托 和 接口.. 在处理相同的时候的效率问题
本人思来想去 还是觉得自己的测试方法不太好, 暂时搁置一下吧. 想到了再说
ok 之前是我的观点.
还有一种观点就是,这些性能损失在实际代码中是微乎其微的.
而是应该在性能和可读性,可维护性上找到一个平衡点
不可否认 lamb表达式的正确使用确实让代码易懂和优美。
a.Select(lamb)
.Where(lamb)
.OrderBy(lamb)
.ToList();
当你使用它的时候有些东西需要注意
1. 这一连串的方法,他当时不会执行,只会在你调用的时候才会执行~
2. 他是循环执行的 第一个元素 select - where - select - orderBy - 第二个元素 重复..
3. 就是他得延迟性(这玩意儿..我没有研究)
有兴趣的可以去看看 姐夫赵的 从.NET中委托写法的演变谈开去(下):性能相关
特别是在使用官方提供的方法的时候,lamb的优势极其明显
当然也有缺点 比如调试,lamb表达式无法调试,所以建议在lamb表达式中不要有复杂逻辑,要不然调试起来够你折腾
over