程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 在C#中實現弱委托

在C#中實現弱委托

編輯:C#入門知識

 在C#中,使用 Delegate d = Object.Method; 的方式創建一個委托,在實現上,這個委托對象內部持有了源對象的一個強引用(System.Object),如果使用者恰好有特殊需求,比如“要求源對象一旦在其他任何地方都不再使用,應該被及時回收。”,那麼,一旦委托對象的生命期足夠長,由於委托內部的強引用存在,源對象的銷毀將被延遲,與使用者預期不符,可能會導致Bug等問題。

  比如,這裡有一個簡單的測試對象:

1 class ClassForDeclTest2 {3     public string GetString() { return "call GetString"; }4     public int AddInt(int a, int b) { return a + b; }5     public void PrintString(string s) { Print(s);  }6 }  這個簡單對象雖然沒有成員數據,但各個方法也都生命為非Static的,因為在這裡我要測試的是和對象綁定的委托,即 public static Delegate CreateDelegate(Type type, object target, string method); 方式創建的委托。另外,AddInt方法中用到了我自定義的一個函數Print,功能如其名。

  下面這段代碼,演示的是強引用委托,和最直接的弱引用委托方式:

 1 Action<string> a = new ClassForDeclTest().PrintString; 2 a("abc"); 3 GC.Collect(); 4 a("abc"); 5  6 WeakReference weakRef = new WeakReference(new ClassForDeclTest()); 7 a = (s) => { object o = weakRef.Target; if (o != null) Print(s);  }; 8 a("def"); 9 GC.Collect();10 a("def");  輸出是:

abcabcdef  "def”只被輸出了一次,是因為,第6行用new ClassForDeclTest()創建的對象並沒有被專門的變量保存下來,所以第9行的GC.Collect()會將這個對象給回收掉,第10行的委托Invoke將判斷WeakReferece.Target為null,於是第10行不輸出任何內容。

  我這篇文章的主要目的,就是要將6、7行的弱委托創建過程提取成一個專門的工具模塊。

  第一次嘗試:

 1 class WeakDelegate 2 { 3     public WeakDelegate(object o, string methodName) : 4         this(o, o.GetType().GetMethod(methodName)) 5     { 6     } 7  8     public WeakDelegate(object o, MethodInfo method) 9     {10         m_target = new WeakReference(o);11         m_method = method;12     }13 14     public object Invoke(params object[] args)15     {16         object target = m_target.Target;17         if (target != null) return m_method.Invoke(target, args);18         else return null;19     }20 21     private WeakReference m_target;22     private MethodInfo m_method;23 }24 25 WeakDelegate d = new WeakDelegate(new ClassForDeclTest(), "PrintString");26 d.Invoke("abc");27 GC.Collect();28 d.Invoke("abc");  這種方法很簡單,只要事件發送方管理一個WeakDelegate的容器,就能很方便的使用弱委托。一個明顯的缺點是,使用這個WeakDelegate類,對事件發送方是侵入性的,如果發送方是系統類型,不能修改,比如按鈕事件 public event EventHandler Click; 要求事件響應者必須是形如 public delegate void EventHandler(Object sender, EventArgs e); 的委托,所以WeakDelegate不能滿足需要。

  第二次嘗試:    

 1 class WeakDelegate 2 { 3     public WeakDelegate(object o, string methodName): 4         this(o, o.GetType().GetMethod(methodName)) 5     { 6     } 7  8     public WeakDelegate(object o, MethodInfo method) 9     {10         m_target = new WeakReference(o);11         m_method = method;12     }13 14     public Delegate ToDelegate()15     {16         ParameterExpression[] parExps = null;17         {18             ParameterInfo[] parInfos = m_method.GetParameters();19             parExps = new ParameterExpression[parInfos.Length];20             for (int i = 0; i < parExps.Length; ++i)21             {22                 parExps[i] = Expression.Parameter(parInfos[i].ParameterType, "p" + i);23             }24         }25 26         Expression target = Expression.Field(Expression.Constant(this), GetType().GetField("m_target", BindingFlags.Instance | BindingFlags.NonPublic));27         target = Expression.Convert(Expression.Property(target, "Target"), m_method.ReflectedType);28 29         Expression body = 30             Expression.Condition(31             Expression.NotEqual(target, Expression.Constant(null)),32             Expression.Call(target, m_method, parExps),33             GetTypeDefaultExpression(m_method.ReturnType));34 35         return Expression.Lambda(body, parExps).Compile();36     }37 38     private static Expression GetTypeDefaultExpression(Type t)39     {40         if (t == typeof(void)) return Expression.Call(typeof(WeakDelegateHelper).GetMethod("EmptyFunc", BindingFlags.NonPublic | BindingFlags.Static));41         else if (t.IsClass) return Expression.Constant(null, t);42         else return Expression.Constant(t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null));43     }44 45     private WeakReference   m_target;46     private MethodInfo      m_method;47 }  這個類的變化是,移除了Invoke方法,添加ToDelegate,後者的功能是根據WeakReference關聯的源對象方法,生成一個Func<>或者Action<>類型的委托。WeakReference對象調用ToDelegate生成的委托,可以被用於各種需要委托的場合,比如上面按鈕的Click事件響應。這個類的用法如:(Func<string>)new WeakDelegate(new ClassForDeclTest(), "GetString").ToDelegate(); 雖然也沒有用專門的變量來存儲WeakDelegate對象,但ToDelegate生成的委托中含有WeakDelegate對象的強引用(Expression.Constant(this)),而WeakDelegate內部又持有源對象的弱引用,故源對象的銷毀並不受影響,能夠達到目的。

  測試如下:

 1 // 輸出一個委托執行時間 2  public static void PerfTimer(Action f, params string[] name) 3 { 4     Assert(name.Length <= 1); 5  6     Stopwatch watch = new Stopwatch(); 7     watch.Start(); 8     f(); 9     watch.Stop();10     float seconds = (watch.ElapsedMilliseconds / 1000.0f);11 12     if (name.Length > 0) Print(name[0], ":", seconds);13     else Print(seconds);14 }15 16 // 性能測試幫助類型17 class ClassForPerfTest18 {19     public int N { get; set; }20     public void Inc() { N += 1; }21 }22 23 ...24 25 {26     Print("-------測試 : Func<string>-------");27 28 &nb

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved