本質上講,代理提供了方法的封裝,它把方法以某種方式封裝在一個代理對象裡,通過代理對象來執行調用方法、移除或者添加代理對象等操作。從另一種意義上講,你可以把代理類型看作單一方法的接口,代理對象看作實現了該接口的對象。
代理對象基礎:代理類型
下面的代碼定義了一個代理類型
[csharp]
internal delegate void Feedback(Int32 value);
C#編譯器實際上將這個代理類型轉化成為一個繼承自MulticastDelegate的類。
[csharp]
internal class Feedback : System.MulticastDelegate
{
// Constructor
public Feedback(Object object, IntPtr method);
// Method with same prototype as specified by the source code
public virtual void Invoke(Int32 value);
// Methods allowing the callback to be called asynchronously
public virtual IAsyncResult BeginInvoke(Int32 value,
AsyncCallback callback, Object object);
public virtual void EndInvoke(IAsyncResult result);
}
MulticastDelegate繼承自Delegate類,下面是代理類的簽名:
[csharp]
public abstract class Delegate : ICloneable, ISerializable
{
protected Delegate(object target, string method);
protected Delegate(Type target, string method);
public static bool operator !=(Delegate d1, Delegate d2);
public static bool operator ==(Delegate d1, Delegate d2);
public MethodInfo Method { get; }
public object Target { get; }
public virtual object Clone();
public static Delegate Combine(params Delegate[] delegates);
public static Delegate Combine(Delegate a, Delegate b);
protected virtual Delegate CombineImpl(Delegate d);
public static Delegate CreateDelegate(Type type, MethodInfo method);
public static Delegate CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure);
public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method);
public static Delegate CreateDelegate(Type type, object target, string method);
public static Delegate CreateDelegate(Type type, Type target, string method);
public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure);
public static Delegate CreateDelegate(Type type, object target, string method, bool ignoreCase);
public static Delegate CreateDelegate(Type type, Type target, string method, bool ignoreCase);
public static Delegate CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure);
public static Delegate CreateDelegate(Type type, Type target, string method, bool ignoreCase, bool throwOnBindFailure);
public object DynamicInvoke(params object[] args);
protected virtual object DynamicInvokeImpl(object[] args);
public override bool Equals(object obj);
public override int GetHashCode();
public virtual Delegate[] GetInvocationList();
protected virtual MethodInfo GetMethodImpl();
public virtual void GetObjectData(SerializationInfo info, StreamingContext context);
public static Delegate Remove(Delegate source, Delegate value);
public static Delegate RemoveAll(Delegate source, Delegate value);
protected virtual Delegate RemoveImpl(Delegate d);
}
這樣的轉化實際上定義了代理對象所有可能的操作,下面將一一列出
一、構造並初始化代理對象
最常見的方式是使用下面的方式來構造並初始化代理對象
[csharp]
Feedback fbInstance = new Feedback(new Program().FeedbackToFile);
然而,隨著C#版本的升級,越來越多和代理有關的特性隨之引入,同時也改變了代理對象的構造並初始化方式,例如在C#3.0中就可以用匿名方法或者lambda表達式來構造並初始化代理對象。
二、調用代理對象封裝的方法
這實際上是調用代理定義時編譯器所生成的Invoke或者BeginInvoke方法,一個用於同步調用,另一個用於異步調用。
[csharp]
fbInstance.Invoke(1);
但是,C#做了進一步優化,你可以直接通過代理對象來調用其所封裝的方法,例如:
[csharp]
fbInstance(1);
三、連接和移除代理對象
前面看到的代理對象都是封裝一個方法,實際上,代理對象可以將一系列方法封裝成一個鏈表,這一特性取決於Delegate類的Combine和Remove方法。
[csharp]
fbChain = (Feedback) Delegate.Combine(fbChain, fb1);
實際上,C#對這一用法也提供了優化,可以使用+、+=、-、-=運算符來添加或移除代理對象:
[csharp]
fbChain += fb1;
代理的擴展:事件
C#事件的引入類似屬性的引入,屬性是對字段的封裝,而事件對象是對代理對象的封裝。
當定義如下事件時:
[csharp]
public event EventHandler<NewMailEventArgs> NewMail;
C#編譯器實際上會將其轉換成如下的代碼:
[csharp]
// 1. A PRIVATE delegate field that is initialized to null
private EventHandler<NewMailEventArgs> NewMail = null;
// 2. A PUBLIC add_Xxx method (where Xxx is the Event name)
// Allows methods to register interest in the event.
public void add_NewMail(EventHandler<NewMailEventArgs> value) {
// The loop and the call to CompareExchange is all just a fancy way
// of adding a delegate to the event in a thread-safe way
EventHandler<NewMailEventArgs>prevHandler;
EventHandler<NewMailEventArgs> newMail = this.NewMail;
do {
prevHandler = newMail;
EventHandler<NewMailEventArgs>newHandler =
(EventHandler<NewMailEventArgs>) Delegate.Combine(prevHandler, value);
newMail = Interlocked.CompareExchange<EventHandler<NewMailEventArgs>>(
ref this.NewMail, newHandler, prevHandler);
} while (newMail != prevHandler);
}
// 3. A PUBLIC remove_Xxx method (where Xxx is the Event name)
// Allows methods to unregister interest in the event.
public void remove_NewMail(EventHandler<NewMailEventArgs> value) {
// The loop and the call to CompareExchange is all just a fancy way
// of removing a delegate from the event in a thread-safe way
EventHandler<NewMailEventArgs> prevHandler;
EventHandler<NewMailEventArgs> newMail = this.NewMail;
do {
prevHandler = newMail;
EventHandler<NewMailEventArgs> newHandler =
(EventHandler<NewMailEventArgs>) Delegate.Remove(prevHandler, value);
newMail = Interlocked.CompareExchange<EventHandler<NewMailEventArgs>>(
ref this.NewMail, newHandler, prevHandler);
} while (newMail != prevHandler);
}
從上面生成的代碼可以看出,事件對象封裝了類中的代理對象,並且只暴露出add_xxx和remove_xxx方法(事件對象的+=和-=即調用相應的方法),而外界無法對其封裝的代理對象進行額外的操作(構建並初始化代理對象、調用代理對象封裝的方法)。