Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 88 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
88
Dung lượng
283,94 KB
Nội dung
Converting Delegate Invocation from Synchronous to Asynchronous | 327 In the TestIndividualInvokesExceptions method of this recipe, if an exception is caught, it is logged to the event log and displayed, then the code continues to invoke delegates. This strategy allows for as fine-grained handling of exceptions as you need. One way to deal with this is to store all of the exceptions that occur during del- egate processing, and then wrap all of the exceptions encountered during processing in a custom exception. After processing completes, throw the custom exception. See the MulticastInvocationException class in the Solution. By adding a finally block to this try-catch block, you could be assured that code within this finally block is executed after every delegate returns. This technique is useful if you want to interleave code between calls to delegates, such as code to clean up objects that are not needed or code to verify that each delegate left the data it touched in a stable state. See Also Recipes 9.1 and 9.2; see the “Delegate Class” and “Delegate.GetInvocationList Method” topics in the MSDN documentation. 9.4 Converting Delegate Invocation from Synchronous to Asynchronous Problem You have determined that one or more delegates invoked synchronously within your application are taking a long time to execute. This delay is making the user interface less responsive to the user. The invocation of these delegates should be converted from synchronous to asynchronous mode. Solution A typical synchronous delegate type and supporting code that invokes the delegate are shown here: public delegate void SyncDelegateTypeSimple( ); public class TestSyncDelegateTypeSimple { public static void Method1( ) { Console.WriteLine("Invoked Method1"); } } The code to use this delegate is: public static void TestSimpleSyncDelegate( ) { 328 | Chapter 9: Delegates, Events, and Lambda Expressions SyncDelegateTypeSimple sdtsInstance = TestSyncDelegateTypeSimple.Method1; sdtsInstance( ); } This delegate can be called asynchronously on a thread obtained from the thread pool by modifying the code as follows: public static void TestSimpleAsyncDelegate( ) { AsyncCallback callBack = new AsyncCallback(DelegateSimpleCallback); SyncDelegateTypeSimple sdtsInstance = TestSyncDelegateTypeSimple.Method1; IAsyncResult asyncResult = sdtsInstance.BeginInvoke(callBack, null); Console.WriteLine("WORKING "); } // The callback that gets called when TestSyncDelegateTypeSimple.Method1 // is finished processing private static void DelegateSimpleCallback(IAsyncResult iResult) { AsyncResult result = (AsyncResult)iResult; SyncDelegateTypeSimple sdtsInstance = (SyncDelegateTypeSimple)result.AsyncDelegate; sdtsInstance.EndInvoke(result); Console.WriteLine("Simple callback run"); } AsyncResult can be found in the System.Runtime.Remoting.Messaging namespace in mscorlib. Of course, you might also want to change the TestSyncDelegateTypeSimple class name to TestAsyncDelegateTypeSimple and the SyncDelegateTypeSimple delegate name to AsyncDelegateTypeSimple just to be consistent with your naming. The previous example shows how to call a delegate that accepts no parameters and returns void. The next example shows a synchronous delegate that accepts parame- ters and returns an integer: public delegate int SyncDelegateType(string message); public class TestSyncDelegateType { public static int Method1(string message) { Console.WriteLine("Invoked Method1 with message: " + message); return 1; } } Converting Delegate Invocation from Synchronous to Asynchronous | 329 The code to use this delegate is: public static void TestComplexSyncDelegate( ) { SyncDelegateType sdtInstance = TestSyncDelegateType.Method1; int retVal = sdtInstance("Synchronous call"); Console.WriteLine("Sync: " + retVal); } The synchronous invocation of the delegate can be converted to asynchronous invo- cation in the following manner: public static void TestCallbackAsyncDelegate( ) { AsyncCallback callBack = new AsyncCallback(DelegateCallback); SyncDelegateType sdtInstance = TestSyncDelegateType.Method1; IAsyncResult asyncResult = sdtInstance.BeginInvoke("Asynchronous call", callBack, null); Console.WriteLine("WORKING "); } // The callback that gets called when TestSyncDelegateType.Method1 // is finished processing private static void DelegateCallback(IAsyncResult iResult) { AsyncResult result = (AsyncResult)iResult; SyncDelegateType sdtInstance = (SyncDelegateType)result.AsyncDelegate; int retVal = sdtInstance.EndInvoke(result); Console.WriteLine("retVal (Callback): " + retVal); } Discussion Converting the invocation of a delegate from being synchronous to asynchronous is not an overly complicated procedure. You need to add calls to both BeginInvoke and EndInvoke on the delegate that is being called synchronously. A callback method, DelegateCallback, is added, which gets called when the delegate is finished. This call- back method then calls the EndInvoke method on the delegate invoked using BeginInvoke. You must always call EndInvoke when invoking delegates asynchro- nously, even when the delegate returns void, to ensure proper cleanup of resources in the CLR. 330 | Chapter 9: Delegates, Events, and Lambda Expressions The notification callback method specified in the callback parameter accepts a sin- gle parameter of type IAsyncResult. This parameter can be cast to an AsyncResult type and used to set up the call to the EndInvoke method. If you want to handle any exceptions thrown by the asynchronous delegate in the notification callback, wrap the EndInvoke method in a try/catch exception handler. See Also The “Delegate Class” and “Asynchronous Delegates” topics in the MSDN documentation. 9.5 An Advanced Interface Search Mechanism Problem You are searching for an interface using the Type class. However, complex interface searches are not available through the GetInterface and GetInterfaces methods of a Type object. The GetInterface method searches for an interface only by name (using a case-sensitive or case-insensitive search), and the GetInterfaces method returns an array of all the interfaces implemented on a particular type. You want a more focused searching mechanism that might involve searching for interfaces that define a method with a specific signature or implemented interfaces that are loaded from the GAC. You need more flexible and more advanced searching for interfaces that does not involve creating your own interface search engine. This capability might be used for applications like a code generator or reverse engineering tool. Solution Use LINQ to query the type interface information and perform rich searches. The method shown in Example 9-3 will demonstrate one complex search that can be per- formed with LINQ. Example 9-3. Performing complex searches of interfaces on a type using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; public class SearchType { public static void FindSpecificInterfaces( ) { // set up the interfaces to search for Type[] interfaces = { typeof(System.ICloneable), typeof(System.Collections.ICollection), An Advanced Interface Search Mechanism | 331 The FindSpecificInterfaces method searches for any of the three interface types contained in the Names array that are implemented by the System.Collections. ArrayList type. It does this by using LINQ to query if the type is an instance of any of the set of interfaces. Discussion There are many ways to use LINQ to search for interfaces implemented on a type— here are just a few other searches that can be performed: • A search for all implemented interfaces that are defined within a particular namespace (in this case, the System.Collections namespace): var collectionsInterfaces = from type in searchType.GetInterfaces( ) where type.Namespace == "System.Collections" select type; • A search for all implemented interfaces that contain a method called Add, which returns an Int32 value: var addInterfaces = from type in searchType.GetInterfaces( ) from method in type.GetMethods( ) where (method.Name == "Add") && (method.ReturnType == typeof(int)) select type; • A search for all implemented interfaces that are loaded from the GAC: var gacInterfaces = from type in searchType.GetInterfaces( ) where type.Assembly.GlobalAssemblyCache select type; • A search for all implemented interfaces that are defined within an assembly with the version number 2.0.0.0: var versionInterfaces = from type in searchType.GetInterfaces( ) where type.Assembly.GlobalAssemblyCache select type; typeof(System.IAppDomainSetup) }; // set up the type to examine Type searchType = typeof(System.Collections.ArrayList); var matches = from t in searchType.GetInterfaces( ) join s in interfaces on t equals s select s; Console.WriteLine("Matches found:"); foreach (Type match in matches) { Console.WriteLine(match.ToString( )); } } } Example 9-3. Performing complex searches of interfaces on a type (continued) 332 | Chapter 9: Delegates, Events, and Lambda Expressions See Also The “Lambda Expressions (C# Programming Guide)” and “where keyword [LINQ] (C#)” topics in the MSDN documentation. 9.6 Observing Additions and Modifications to Dictionaries Problem You have multiple objects that need to observe modifications to objects that imple- ment IDictionary<K,V>. When an item is added or modified in the dictionary-based collection, each of these observer objects should be able to vote to allow or disallow the action. In order for an action to be allowed to complete, all observer objects must state if they are vetoing the action. If even one observer object votes to disallow the action, the action is prevented. Solution Use the ObservableDictionaryObserver class implemented in Example 9-5 to observe additions and modifications to the ObservableDictionary class (shown in Example 9-4) object that is registered with this object. The ObservableDictionary class is a generic wrapper for collections that implement IDictionary<K,V> and allows itself to be observed by the ObservableDictionaryObserver class. The ObservableDictionaryEventArgs class is a specialization of the EventArgs class, which provides the IDictionary<K,V> key and value being added or modified to the ObservableDictionaryObserver object, as well as a Boolean property, KeepChanges. This flag indicates whether the addition or modification in the ObservableDictionary object will succeed or be rolled back. The MakeObservableDictionary extension method for IDictionary<K,V> wraps up the code for creating an ObservableDictionary from an IDictionary instance. Example 9-4 illustrates the two classes and the extension method. Example 9-4. ObservableDictionary and ObservableDictionaryEventArgs classes and the MakeObservableDictionary extension method public class ObservableDictionary<TKey,TValue> : IDictionary<TKey,TValue> { IDictionary<TKey, TValue> _internalDictionary; public ObservableDictionary(IDictionary<TKey,TValue> dictionary) { if (dictionary == null) throw new ArgumentNullException("dictionary"); _internalDictionary = dictionary; } Observing Additions and Modifications to Dictionaries | 333 #region Events and Event Initiation public event EventHandler<ObservableDictionaryEventArgs<TKey,TValue>> AddingEntry; public event EventHandler<ObservableDictionaryEventArgs<TKey, TValue>> AddedEntry; public event EventHandler<ObservableDictionaryEventArgs<TKey, TValue>> ChangingEntry; public event EventHandler<ObservableDictionaryEventArgs<TKey, TValue>> ChangedEntry; protected virtual bool OnAdding(ObservableDictionaryEventArgs<TKey,TValue> e) { if (AddingEntry != null) { AddingEntry(this, e); return (e.KeepChanges); } return (true); } protected virtual void OnAdded(ObservableDictionaryEventArgs<TKey, TValue> e) { if (AddedEntry != null) { AddedEntry(this, e); } } protected virtual bool OnChanging(ObservableDictionaryEventArgs<TKey, TValue> e) { if (ChangingEntry != null) { ChangingEntry(this, e); return (e.KeepChanges); } return (true); } protected virtual void OnChanged(ObservableDictionaryEventArgs<TKey, TValue> e) { if (ChangedEntry != null) { ChangedEntry(this, e); } } #endregion // Events and Event Initiation #region Interface implementations #region IDictionary<TKey,TValue> Members public ICollection<TValue> Values { Example 9-4. ObservableDictionary and ObservableDictionaryEventArgs classes and the MakeObservableDictionary extension method (continued) 334 | Chapter 9: Delegates, Events, and Lambda Expressions get { return _internalDictionary.Values; } } public ICollection<TKey> Keys { get { return _internalDictionary.Keys; } } public TValue this[TKey key] { get { TValue value; if (_internalDictionary.TryGetValue(key, out value)) return value; else { return default(TValue); } } set { // see if this key is there to be changed, if not add it if (_internalDictionary.ContainsKey(key)) { ObservableDictionaryEventArgs<TKey, TValue> args = new ObservableDictionaryEventArgs<TKey, TValue>(key, value); if (OnChanging(args)) { _internalDictionary[key] = value; } else { Debug.WriteLine("Change of value cannot be performed"); } OnChanged(args); } else { Debug.WriteLine("Item did not exist, adding"); _internalDictionary.Add(key, value); } } } public void Add(TKey key, TValue value) { ObservableDictionaryEventArgs<TKey, TValue> args = new ObservableDictionaryEventArgs<TKey, TValue>(key, value); Example 9-4. ObservableDictionary and ObservableDictionaryEventArgs classes and the MakeObservableDictionary extension method (continued) Observing Additions and Modifications to Dictionaries | 335 if (OnAdding(args)) { this._internalDictionary.Add(key, value); } else { Debug.WriteLine("Addition of key/value cannot be performed"); } OnAdded(args); } public bool ContainsKey(TKey key) { return _internalDictionary.ContainsKey(key); } public bool Remove(TKey key) { return _internalDictionary.Remove(key); } public bool TryGetValue(TKey key, out TValue value) { return _internalDictionary.TryGetValue(key, out value); } #endregion #region ICollection<KeyValuePair<TKey,TValue>> Members public void Add(KeyValuePair<TKey, TValue> item) { _internalDictionary.Add(item.Key, item.Value); } public void Clear( ) { _internalDictionary.Clear( ); } public bool Contains(KeyValuePair<TKey, TValue> item) { return _internalDictionary.Contains(item); } public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { _internalDictionary.CopyTo(array, arrayIndex); } Example 9-4. ObservableDictionary and ObservableDictionaryEventArgs classes and the MakeObservableDictionary extension method (continued) 336 | Chapter 9: Delegates, Events, and Lambda Expressions public int Count { get { return _internalDictionary.Count; } } public bool IsReadOnly { get { return _internalDictionary.IsReadOnly; } } public bool Remove(KeyValuePair<TKey, TValue> item) { return _internalDictionary.Remove(item); } #endregion #region IEnumerable<KeyValuePair<TKey,TValue>> Members public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator( ) { return _internalDictionary.GetEnumerator( ); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator( ) { return _internalDictionary.GetEnumerator( ); } #endregion #endregion // Interface implementations } public static ObservableDictionary<TKey, TValue> MakeObservableDictionary<TKey, TValue>( this IDictionary<TKey, TValue> dictionary) { return new ObservableDictionary<TKey, TValue>(dictionary); } public class ObservableDictionaryEventArgs<TKey, TValue> : EventArgs { TKey _key; TValue _value; public ObservableDictionaryEventArgs(TKey key, TValue value) { _key = key; Example 9-4. ObservableDictionary and ObservableDictionaryEventArgs classes and the MakeObservableDictionary extension method (continued) [...]... 353 // Invoke delegate work = "WorkStarted"; i = drw(ref work); Console.WriteLine(work); While it is possible to declare a delegate with the params modifier, you cannot hook up the delegate using a lambda expression with the params keyword in the parameter list You get the CS 152 5 Invalid expression term 'params' compiler error on the DoParamsWork line: // Done as a lambda expression you get CS 152 5... commission of : $12,2 75. 00 Ray made a commission of : $2,9 75. 00 Biff made a commission of : $124.63 Annual Earnings were $232,000.00 Paid Chas $22,8 75. 00 to produce $77,333.33 FIRE Chas! Paid Ray $4,600.00 to produce $77,333.33 Paid Biff $ 254 .33 to produce $77,333.33 360 | Chapter 9: Delegates, Events, and Lambda Expressions Discussion One of the best ways we’ve heard of to describe closures in C# is to think... Sales of $ 65, 000.00: Chas made a commission of : $6,900.00 Ray made a commission of : $1,6 25. 00 Biff made a commission of : $70. 25 Quarterly Sales of $20,000.00: Chas made a commission of : $0.00 Ray made a commission of : $0.00 Biff made a commission of : $20.00 Quarterly Sales of $37,000.00: Chas made a commission of : $3,700.00 Ray made a commission of : $0.00 Biff made a commission of : $39. 45 Quarterly... calling through the delegate, so what matters is whether that delegate has the params keyword or not See Also Recipe 9.11; the “CS1670,” “CS 152 5,” “CS1628,” “out,” “ref,” “params,” and “System.ParamArrayAttribute” topics in the MSDN documentation 9.10 Using Closures in C# Problem You want to associate a small amount of state with some behavior without going to the trouble of building a new class Solution... quarterlyEarnings = { new QuarterlyEarning( ){ new QuarterlyEarning( ){ new QuarterlyEarning( ){ new QuarterlyEarning( ){ 15m}}; 358 | Name="Q1", Name="Q2", Name="Q3", Name="Q4", Chapter 9: Delegates, Events, and Lambda Expressions Earnings Earnings Earnings Earnings = = = = 650 00m, Rate = 0.1m }, 20000m, Rate = 0.1m }, 37000m, Rate = 0.1m }, 110000m, Rate = 0 var calculators = from e in quarterlyEarnings... See Also The “Event Keyword,” “EventHandler Delegate,” “EventArgs Class,” and “Handling and Raising Events” topics in the MSDN documentation 9.7 Using Lambda Expressions Problem There is a feature in C# 3.0 called lambda expressions While lambda expressions can be viewed as syntactic sugar for making anonymous method definition less difficult, you want to understand all of the different ways that they... extend their lifetime, which makes closures possible in C# To show an example of this, you will build a quick reporting system that tracks sales personnel and their revenue production versus commissions The closure behavior is that you can build one bit of code that does the commission calculations per quarter and works on every salesperson 356 | Chapter 9: Delegates, Events, and Lambda Expressions... work of calculating the commissions: delegate void CalculateEarnings(SalesWeasel weasel); static CalculateEarnings GetEarningsCalculator(decimal quarterlySales, decimal bonusRate) { Using Closures in C# | 357 return salesWeasel => { // Figure out the weasel's quota for the quarter decimal quarterlyQuota = (salesWeasel.AnnualQuota / 4); // Did he make quota for the quarter? if (quarterlySales < quarterlyQuota)... weasels SalesWeasel[] weasels = { new SalesWeasel { Name="Chas", AnnualQuota=100000m, CommissionRate=0.10m }, new SalesWeasel { Name="Ray", AnnualQuota=200000m, CommissionRate=0.025m }, new SalesWeasel { Name="Biff", AnnualQuota =50 000m, CommissionRate=0.001m }}; Then set up the earnings calculators based on quarterly earnings: public class QuarterlyEarning { public string Name { get; set; } public decimal... win!"); DailyBitFlash.TransmitBreakingNews("New pres coming in 08."); DailyBitFlash.TransmitBreakingNews("VS2008 & NET 3 .5 Rocks LA"); } private static void BreakingNews(object src, NewsEventArgs nea) { Console.WriteLine(nea.LatestNews); } } Set Up Event Handlers Without the Mess | 351 Discussion The main benefit of using the generic EventHandler instead of System.EventHandler is that you write less . } } } Example 9 -3. Performing complex searches of interfaces on a type (continued) 33 2 | Chapter 9: Delegates, Events, and Lambda Expressions See Also The “Lambda Expressions (C# Programming Guide)”. for all implemented interfaces that are defined within an assembly with the version number 2 .0. 0 .0: var versionInterfaces = from type in searchType.GetInterfaces( ) where type.Assembly.GlobalAssemblyCache . asynchro- nously, even when the delegate returns void, to ensure proper cleanup of resources in the CLR. 33 0 | Chapter 9: Delegates, Events, and Lambda Expressions The notification callback method specified