委托的雜七雜八---《clr via c#》筆記
2012-01-31 22:35 海不是藍 閱讀(477) 評論(0) 收藏 舉報
|
初識委托 |
委托提供了一種回調的函數機制,委托確保回調的方法是類型安全的,clr最重要的目標之一是類型安全。
非托管的c/c++中回調函數只是個內存地址。
blah blah blah......
什么委托基本語法啊,什么委托回調靜態回調實例方法啊!啊!啊!
blah blah blah......
blah blah blah......
blah blah blah......
|
委托揭秘 |
定義一個委托clr所做的幕后工作
public delegate void Mydelegate(Int32 i);
編譯器生成的代碼
public class Mydelegate : System.MulticastDelete
{
public Mydelegate(Int32 i, IntPtr method);
public virtual string Invoke(Int32 i);
public virtual IAsyncResult BeginResult(Int32 i, AsyncCallback callback, Object object);
public virtual void EndInvoke(IAsyncResult result);
}

委托的繼承關系
我們的委托繼承--->System.MulticastDelete
System.MulticastDelete繼承--->System.Delegate
System.Delegate繼承--->System.object
委托放到什么地方?
有些人把委托放到和class同級的地方,也就是全局,有些人把委托放到class里面,到底怎么放才對?
Jeffrey說:"隨便你娃怎么放!你可以把它嵌套到類型中!你也可以把他放到全局范圍中定義!委托是類,能夠定義類的地方都可以定義委托。"
委托的可訪問性
1.把委托放到全局范圍,和class同級
public delegate void Mydelegate(Int32 i);
class Program
{
static void Main(){}
}
這個時候il代碼

所以種情況委托是公開的。
2.嘗試下把委托定義為私有的
private delegate void Mydelegate(Int32 i);
class Program
{
static void Main(){}
}
嘗試編譯代碼,但是編譯器報錯
錯誤1命名空間中定義的元素無法顯式聲明為 private、protected 或 protected internal
結果很悲劇!!!!
3.把委托放到類里面
class Program
{
public delegate void Mydelegate(Int32 i);
static void Main() { }
}

這個時候委托是公開的,可以在其他地方調用這個委托
public class a
{
public void test()
{
Program.Mydelegate my = test1;
my(2);
}
public void test1(Int32 i) { Console.WriteLine(i); }
}
4.特殊的情況
public class a
{
public delegate void Mydelegate(Int32 i);
public event Mydelegate Myevent;
}
當類中有個委托類型的事件,那么外部就不能直接使用委托了,這個時候就相當于委托是字段,而事件是屬性。那么就體現了封裝性了。
你可以試試在其它類中來訪問這個委托,完全不給你機會!只能通過事件。
|
System.MulticastDelete |
由于所有委托類型都派生自System.MulticastDelete,逼不得已要研究它!!!
它有3個重要的非公共字段,我們定義的委托類型繼承了這3個字段。
|
字段 |
類型 |
說明 |
|
_target |
System.Object |
委托對象包裝一個靜態方法的時候為null,實例方法的時候是回調方法要操作的對象,也就是實例方法的this值 |
|
_methodPtr |
System.IntPtr |
一個內部整數值,clr用它標識要回調的方法 |
|
_invocationLis |
System.Object |
一般是null,構造一個委托鏈時,它可以引用一個委托數組 |
說了字段現在說說2個主要的屬性Target和Method
Target返回一個引用,它指向回調方法要操作的對象。
Method要回調方法的內存地址
還有就是委托類型的Invoke方法,就是它去調用那個要回調的方法。
委托對象 a,可以寫a();也可以直接去a.Invoke();
我瘋了才去用a.Invoke(),浪費鍵盤壽命
|
委托鏈 |
童鞋們!委托的+=,-=是不是用得很爽,其實最開始沒得這個!讓你回到解放前
public static Delegate Combine(Delegate a, Delegate b);
上面那個就是+=的祖宗老大爺!
public delegate void Mydelegate();
class Program
{
static void Main()
{
Mydelegate my1 = new Mydelegate(test1);
Mydelegate my2 = new Mydelegate(test2);
Mydelegate my3 = new Mydelegate(test3);
Mydelegate my = null;
my = (Mydelegate)Delegate.Combine(my, my1);
my = (Mydelegate)Delegate.Combine(my, my2);
my = (Mydelegate)Delegate.Combine(my, my3);
my();
Console.WriteLine("------絲襪般的分割線------");
my = (Mydelegate)Delegate.Remove(my, my1);
my = (Mydelegate)Delegate.Remove(my, new Mydelegate(test2));
my();
Console.Read();
}
public static void test1() { Console.WriteLine("1"); }
public static void test2() { Console.WriteLine("2"); }
public static void test3() { Console.WriteLine("3"); }
}
上面的代碼就是委托鏈的解放前寫法!爽吧!
偉大的微軟為了解放我們這些苦命的程序員,就搞了+=和-=這2個操作符。
委托鏈不爽的地方!
如果委托鏈中一個方法異常或者堵塞很久,那么后面的方法都沒法調用!所以默認的委托鏈調用算法不夠健壯。
然后下面是Jeffrey的解決思路,擦 加了個try。我忍!
public delegate void Mydelegate();
class Program
{
static void Main()
{
a a1 = new a();
Mydelegate my1 = new Mydelegate(a1.test1);
Mydelegate my2 = new Mydelegate(a1.test2);
Mydelegate my3 = new Mydelegate(a1.test3);
Mydelegate my = null;
my = (Mydelegate)Delegate.Combine(my, my1);
my = (Mydelegate)Delegate.Combine(my, my2);
my = (Mydelegate)Delegate.Combine(my, my3);
Show(my);
Console.Read();
}
static void Show(Mydelegate my)
{
foreach (Mydelegate d in my.GetInvocationList())
{
try
{
d();
}
catch (InvalidOperationException e)
{
Console.WriteLine(e.Message);
Console.WriteLine(d.Target);
}
}
}
}
public class a
{
public void test1() { Console.WriteLine("1"); }
public void test2() { Console.WriteLine("2"); }
public void test3() { throw new InvalidOperationException("blah blah blah......"); }
}

委托與反射先欠著,我不是機器人

|
**************************** |
祖賢今天45歲了。

浙公網安備 33010602011771號