天天看點

事件與委托(一)

委托是一個對象,它知道如何調用一個方法。

委托的定義: 委托類型定義了委托執行個體可以調用的那類方法,具體來說,委托類型定義了方法的傳回類型和參數。

​<code>​delegate​</code>​​<code>​string​</code>​​<code>​Hello(string name);​</code>​

​<code>​string​</code>​​<code>​Splicing(string name){​</code>​​<code>​return $"hello,{ name }.";​</code>​​<code>​}​</code>​

把方法指派給委托變量的時候就建立了委托執行個體。

​<code>​Hello hello =​</code>​​<code>​Splicing;​</code>​

調用

​<code>​string resultName = hello("bob");//輸出 hello bob​</code>​

委托的執行個體其實就是調用者的委托:調用者調用委托,然後委托調用目标方法。

間接的把調用者和目标方法解耦合了。

方法是在運作時才指派給委托變量的。

所有的委托執行個體都具有多點傳播的能力。一個委托執行個體可以引用一組目标方法。

引用一組方法的示例:

+和+= 還有 -和-=操作符号可以合并和解除委托執行個體。

DemoDelegate dd = DemoMethond1; DemoDelegate dd += DemoMethond2;

調用委托dd就回調用DemoMethond1和DemoMethond2

委托的調用順序與他們的定義順序一緻

和 -=會把右邊的委托從左邊的委托裡移除

​<code>​DemoDelegate dd -=​</code>​​<code>​DemoMethond1;​</code>​

委托變量使用+或+=操作符時,其操作數可以是null。就相當于把一個新的值賦給了委托變量。

DemoDelegate dd = null; dd += DemoMethond1;

相當于

​<code>​dd =​</code>​​<code>​DemoMethond1;​</code>​

對單個目标方法的委托變量使用-=操作符時,就相當于把null值指派給了委托變量。

委托是不可變的

使用-=或+=操作符時,實際上時建立了新的委托執行個體,并把它賦給目前的委托變量。

如果多點傳播委托的傳回值不是void,那麼調用者從最後一個被調用的方法來接收傳回值。前面的方法仍然會被調用,但是其傳回值就被棄用了。

所有的委托類型都派生于system.MulticastDelegate,而它又派生于system.delegate

c#會把作用于委托的+、-、-=、+=操作編譯成使用system.delegate的combine和remove兩個靜态方法。

執行個體方法目标和靜态方法目标

當一個執行個體方法被指派給委托對象的時候,這個委托對象不僅要保留着對方法的引用,還要保留着方法所屬執行個體的引用。

system.delegate的Target屬性就代表着這個執行個體。

如果引用的是靜态方法,那麼Target屬性值就是null。

​<code>​public​</code>​​<code>​delegate​</code>​​<code>​void​</code>​​<code>​Reporter(int result);​</code>​

​<code>​public​</code>​​<code>​class A​</code>​

​<code>​{​</code>​

​<code>​public​</code>​​<code>​void​</code>​​<code>​AReporter(int result)​</code>​

​<code>​Console.WriteLine(result);​</code>​

​<code>​}​</code>​

​<code>​public​</code>​​<code>​class​</code>​​<code>​Program​</code>​

​<code>​static​</code>​​<code>​void​</code>​​<code>​Main()​</code>​

​<code>​A a =​</code>​​<code>​new A();​</code>​

​<code>​Reporter r = x.AReporter;​</code>​

​<code>​r(50);​</code>​

​<code>​Console.WriteLine(r.Target​</code>​​<code>​== x);//True​</code>​

​<code>​Console.WriteLine(p.Method);//void AReporter(Int32)​</code>​

委托類型可以包含泛型類型

​<code>​public​</code>​​<code>​delegate T Mydelegate&lt;T&gt;(T parameter);​</code>​

Func和Action委托

使用泛型委托,他們可以調用的方法可以擁有任意傳回值類型和任意合理數量的參數。

​<code>​//1.這裡表示的是,隻有一個帶泛型T傳回值類型的委托​</code>​

​<code>​delegate​</code>​​<code>​TResult​</code>​​<code>​Func&lt;out​</code>​​<code>​TResult&gt;();​</code>​

​<code>​//2.這裡表示的是,有一個帶泛型T傳回值類型和一個帶泛型入參的委托(入參數量最多16個)​</code>​

​<code>​delegate​</code>​​<code>​TResult​</code>​​<code>​Func&lt;in T,out​</code>​​<code>​TResult&gt;(T parameter);​</code>​

​<code>​//3.這裡表示的是,一個無參無傳回值類型的委托​</code>​

​<code>​delegate​</code>​​<code>​void​</code>​​<code>​Action();​</code>​

​<code>​//4.這裡表示的是,一個入參無傳回值類型的委托(入參數量最多16個)​</code>​

委托可以解決的問題,接口都可以解決。

什麼情況下更适合委托而不是接口呢?當下列條件滿足其中之一時:

接口隻能定義一個方法

需要多點傳播能力

訂閱者需要多次實作接口

​<code>​class A :​</code>​​<code>​ICalculation​</code>​

​<code>​public​</code>​​<code>​int​</code>​​<code>​Calculation(int x)=&gt; x*x;​</code>​

​<code>​class B :​</code>​​<code>​ICalculation​</code>​

​<code>​public​</code>​​<code>​int​</code>​​<code>​Calculation(int x)=&gt; x*x*x;​</code>​

&lt;

委托類型之間互不相容,即使方法簽名一樣

​<code>​delegate​</code>​​<code>​void​</code>​​<code>​Demo1();​</code>​

​<code>​delegate​</code>​​<code>​void​</code>​​<code>​Demo2();​</code>​

​<code>​Demo1 d1 =​</code>​​<code>​Method1;​</code>​

​<code>​Demo2 d2 = d1;// error​</code>​

如果委托執行個體擁有相同的方法目标,那麼委托執行個體就認為是相等的。

當你調用一個方法時,你提供的參數(argument)可以比方法的參數(parameter)定義更具體。

委托可以接受比她的方法目标更具體的參數類型,這個叫ContraVariance

和泛型參類型一樣,委托的Variance僅支援引用轉換

​<code>​delegate​</code>​​<code>​void​</code>​​<code>​StringAction(string s);​</code>​

​<code>​class​</code>​​<code>​Demo​</code>​

​<code>​StringAction sa =​</code>​​<code>​new​</code>​​<code>​StringAction(ActOnObject);​</code>​

​<code>​sa("hello");​</code>​

​<code>​static​</code>​​<code>​void​</code>​​<code>​ActOnObject(object o)=&gt;Console.WriteLine(o);//hello​</code>​

調用方法時,你可以得到一個比請求的類型更具體的類型傳回結果。

委托的目标方法可以傳回比委托描述裡更具體的類型的傳回結果Covariance.

Covariance,out; ContraVariance,in;

<code>delegate</code><code>object</code><code>ObjectRetriever(string s);</code>

<code>class</code><code>Demo</code>

<code>{</code>

<code>static</code><code>void</code><code>Main()</code>

<code>ObjectRetriever o =</code><code>new</code><code>ObjectRetriever(RetrieverString);</code>

<code>object result = o();</code>

<code>Console.WriteLine(result);</code>

<code>}</code>

<code>static</code><code>string</code><code>RetrieverString()=&gt;"hello";</code>

繼續閱讀