委託,不知道你是否有也這樣理解(二)

K戰神發表於2015-04-26

目錄

  • 泛型委託
  • 簡化語法
  • 委託與反射

相關文章連結:

事件,你是否也這樣理解 

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);

 

相關文章