目錄
- 泛型委託
- 簡化語法
- 委託與反射
相關文章連結:
事件,你是否也這樣理解
http://www.cnblogs.com/sunchong/p/sunchong.html
委託,你是否也這樣理解(一)
http://www.cnblogs.com/sunchong/p/3480612.html
一、泛型委託
我們知道泛型能夠提高效率,避免程式碼重複,靈活。
C#提供了無返回值的泛型委託:Action
public delegate void Action<in T>(T obj);
從上我們看到,輸入引數是obj,無返回值。T是泛型佔位符,輸入引數型別並可以協變。
像這樣的委託c#為我們提供了16個,可以傳遞1~16個輸入引數。
C#也提供了有返回值的泛型委託:Func
public delegate TResult Func<in T, out TResult>(T arg);
可以協變的輸入引數arg,逆變的返回引數TResult。
這樣的泛型委託也是16個,也可以傳遞1~16個輸入引數。外加一個返回引數。返回引數型別位於泛型佔位符<in T,inT1,in T2,...,out TResult>最後一個引數TResult。
二、簡化語法
1、使用ThreadPool
public void PutMsg() { System.Threading.ThreadPool.QueueUserWorkItem(DelegateTest.PutMsg,"K1"); }
細看一下定義:
public static bool QueueUserWorkItem(WaitCallback callBack, object state);
public delegate void WaitCallback(object state);
很顯然,WaitCallback是一個又一個輸入引數並無返回值的委託,而引數state就是方法需要傳入的引數。
這樣的方式有侷限性,因為必須是一個輸入引數並無返回值。
三、使用Lambda表示式
public void PutMsg() { System.Threading.ThreadPool.QueueUserWorkItem(msg=>Console.WriteLine(msg.ToString()),"K1"); }
編譯器會為我們自動建立一個私有方法,這個方法名我們不會知道,因為編譯器會根據他自己的規則自動生成。
我們也會將這樣的方法稱之為匿名方法。傳遞一個Object型別的引數,在=>後面的就是方法體。通過IL就會看到生成的程式碼。。
[CompilerGenerated] private static void <PutMsg>b__0(object msg) { Console.WriteLine(msg.ToString()); }
上面就是自己本地的IL程式碼,我們可以看到我們之前定義的msg和=>的方法體。此外還有一個特性[CompilerGenerated],這說明此特性是編譯器生成的程式碼,不是我們自己寫的。
四、區域性變數無需包裝到類中就可以傳遞給回撥方法
public class AClass { public static void UsingLocalVariablesInTheCallbackCode(Int32 numToDo) { Int32[] squares = new Int32[numToDo]; System.Threading.AutoResetEvent done = new System.Threading.AutoResetEvent(false); for (int n = 0; n < squares.Length; n++) { System.Threading.ThreadPool.QueueUserWorkItem(obj => { int num = (int)obj; squares[num] = num * num; if (System.Threading.Interlocked.Decrement(ref numToDo) == 0) { done.Set(); } }, n); } done.WaitOne(); } }
首先大體分析一下,在這個方法中,有一個引數:numToDo,兩個區域性變數:squares,done。關鍵是下面這個Lambda方式的回撥函式,而我們知道使用Lambda就類似又構造一個方法。現在的問題是,通過Lamdba方式生成的方法引用了在UsingLocalVariablesInTheCallbackCode(Int32 numToDo)方法定義的區域性變數。我們知道同一個類中的兩個方法,一個方法無法直接引用另一個方法的區域性變數。那為什麼以上可以直接引用?而且還讓我們感覺用得理所當然。好吧,編譯器確實又為我們做了很多工作,如果我們想讓一個方法引用到另一方法中的區域性變數,那就將這些區域性變數打包到一個類中,通過傳遞此類對引數進行賦值和取值。
四、委託與反射
一般情況下我們會知道回到函式的引數個數和型別,但是如果我們不確定這些,那就通過反射讓編譯器來生成我們所需要的。
C#的Delete類為我們提供了委託反射(下面只是其中一個),通過傳遞委託型別和註冊的方法名,對方法進行註冊。
public static Delegate CreateDelegate(Type type, MethodInfo method);
通過DynamicInvoke來進行呼叫。
public object DynamicInvoke(params object[] args);