1. Trang chủ
  2. » Công Nghệ Thông Tin

C# 3.0 Cookbook phần 5 ppsx

88 444 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

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

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Solution A typical synchronous delegate type and supporting code that invokes the delegate are shown here: public delegate void SyncDelegateTypeSimple ; public class TestSyncDelegateType

Trang 1

Converting Delegate Invocation from Synchronous to Asynchronous | 327

In the TestIndividualInvokesExceptions method of thisrecipe, if an exception is caught, it islogged to the event log and displayed, then the code continuesto invoke delegates This strategy allows for as fine-grained handling of exceptions as you need One way to deal with thisisto store all of the exceptionsthat 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 theMulticastInvocationException class in the Solution.

By adding a finallyblock to this try-catchblock, you could be assured that code within this finallyblock isexecuted after every delegate returns Thistechnique is useful if you want to interleave code between calls to delegates, such as code to clean

up objectsthat are not needed or code to verify that each delegate left the data it touched in a stable state.

Solution

A typical synchronous delegate type and supporting code that invokes the delegate are shown here:

public delegate void SyncDelegateTypeSimple( );

public class TestSyncDelegateTypeSimple

The code to use this delegate is:

public static void TestSimpleSyncDelegate( )

{

Trang 2

SyncDelegateTypeSimple sdtsInstance = TestSyncDelegateTypeSimple.Method1;

AsyncCallback callBack = new AsyncCallback(DelegateSimpleCallback);

SyncDelegateTypeSimple sdtsInstance = TestSyncDelegateTypeSimple.Method1;

The previousexample showshow to call a delegate that acceptsno parametersand returnsvoid The next example shows a synchronous delegate that accepts parame- ters and returns an integer:

public delegate int SyncDelegateType(string message);

public class TestSyncDelegateType

Trang 3

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

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 callsto bothBeginInvokeandEndInvoke on the delegate that is being called synchronously A callback method,DelegateCallback, isadded, which getscalled when the delegate isfinished Thiscall- back method then callsthe EndInvoke method on the delegate invoked usingBeginInvoke.

You must always call EndInvoke when invoking

delegatesasynchro-nously, even when the delegate returns void, to ensure proper cleanup

of resources in the CLR

Trang 4

The notification callback method specified in thecallback parameter acceptsa gle parameter of type IAsyncResult Thisparameter can be cast to an AsyncResulttype and used to set up the call to theEndInvokemethod If you want to handle any exceptionsthrown by the asynchronousdelegate in the notification callback, wrap theEndInvoke method in atry/catch exception handler.

a case-sensitive or case-insensitive search), and theGetInterfacesmethod returnsan array of all the interfacesimplemented 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 doesnot involve creating your own interface search engine Thiscapability 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

Trang 5

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.ArrayListtype It does this by using LINQ to query if the type is an instance of any

of the set of interfaces.

var collectionsInterfaces = from type in searchType.GetInterfaces( )

where type.Namespace == "System.Collections"

select type;

• A search for all implemented interfaces that contain a method calledAdd, which returns anInt32 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( )

// 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

Trang 6

Use theObservableDictionaryObserver class implemented in Example 9-5 to observe additionsand modificationsto theObservableDictionaryclass (shown in Example 9-4) object that isregistered with thisobject TheObservableDictionary class is a generic wrapper for collectionsthat implement IDictionary<K,V> and allowsits elf to be observed by theObservableDictionaryObserver class.

The ObservableDictionaryEventArgs class is a specialization of theEventArgs class, which providestheIDictionary<K,V>key and value being added or modified to theObservableDictionaryObserver object, aswell asa Boolean property, KeepChanges Thisflag indicateswhether the addition or modification in theObservableDictionaryobject will succeed or be rolled back The MakeObservableDictionary extension method for IDictionary<K,V> wrapsup the code for creating anObservableDictionaryfrom anIDictionaryinstance 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)

Trang 7

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)

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

Trang 8

get { return _internalDictionary.Values; }

ObservableDictionaryEventArgs<TKey, TValue> args =

new ObservableDictionaryEventArgs<TKey, TValue>(key, value);

ObservableDictionaryEventArgs<TKey, TValue> args =

new ObservableDictionaryEventArgs<TKey, TValue>(key, value);

Example 9-4 ObservableDictionary and ObservableDictionaryEventArgs classes and the MakeObservableDictionary extension method (continued)

Trang 9

Observing Additions and Modifications to Dictionaries | 335

#region ICollection<KeyValuePair<TKey,TValue>> Members

public void Add(KeyValuePair<TKey, TValue> item)

Example 9-4 ObservableDictionary and ObservableDictionaryEventArgs classes and the

MakeObservableDictionary extension method (continued)

Trang 10

public int Count

#region IEnumerable<KeyValuePair<TKey,TValue>> Members

public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator( )

Trang 11

Observing Additions and Modifications to Dictionaries | 337

Example 9-5 shows the code for theObservableDictionaryObserver class.

_value = value;

this.KeepChanges = true;

}

public bool KeepChanges { get; set; }

public TKey Key { get { return _key; } }

public TValue Value { get { return _value; } }

}

Example 9-5 ObservableDictionaryObserver class

// The observer object that will observe a registered ObservableDictionary object

public class ObservableDictionaryObserver<TKey,TValue>

{

public ObservableDictionaryObserver( ) { }

// set up delegate/events for approving an addition or change

public delegate bool Approval(object sender,

ObservableDictionaryEventArgs<TKey,TValue> e);

public Approval ApproveAdd { get; set; }

public Approval ApproveChange { get; set; }

public void Register(ObservableDictionary<TKey, TValue> dictionary)

Example 9-4 ObservableDictionary and ObservableDictionaryEventArgs classes and the

MakeObservableDictionary extension method (continued)

Trang 12

// check everyone who wants to approve

foreach (Approval approvalInstance in

approval.GetInvocationList( ))

{

if (!approvalInstance(this,args))

{

// if any of the concerned parties

// refuse, then no add Adds by default

private void OnAddedListener(object sender,

ObservableDictionaryEventArgs<TKey, TValue> args)

{

Debug.WriteLine("[NOTIFY] After Add: Item approved for adding: " + args.KeepChanges.ToString( ));

}

private void OnChangingListener(object sender,

ObservableDictionaryEventArgs<TKey, TValue> args)

Trang 13

Observing Additions and Modifications to Dictionaries | 339

Discussion

The observer design pattern allows one or more observer objects to act as spectators

over one or more subjects Not only do the observer objects act as spectators, but they can also induce change in the subjects According to this pattern, any subject is allowed to register itself with one or more observer objects Once this is done, the subject can operate as it normally does The key feature is that the subject doesn’t have to know what it is being observed by; this allows the coupling between subjects and observers to be minimized The observer object(s) will then be notified of any changes in state to the subjects When the subject’s state changes, the observer object(s) can change the state of other objects in the system to bring them into line with changes that were made to the subject(s) In addition, the observer could even make changes or refuse changes to the subject(s) themselves.

The observer pattern is best implemented with events in C# The event object vides a built-in way of implementing the observer design pattern This recipe imple- mentsthispattern on all collectionssupportingIDictionary<K,V> The object being observed must raise events for any listening observer objects to handle, but theIDictionary<K,V>interface found in the FCL doesnot indicate any events In order to make the IDictionary<K,V> raise events at specific times, you must implement a wrapper class, ObservableDictionary, that implementsthe IDictionary<K,V> inter- face ThisObservableDictionaryclassoverridestheAddand indexer membersof the base interface In addition, four events (AddingEntry,AddedEntry,ChangingEntry, andChangedEntry) are created; they will be raised before and after items are added or modified in the ObservableDictionary To raise these events, the following four methods are created, one to raise each event:

pro-• TheOnAdding method raises theAddingEntry event.

• TheOnAdded method raises theAddedEntry event.

• TheOnChanging method raises theChangingEntry event.

• TheOnChanged method raises theChangedEntry event.

}

Debug.WriteLine("[NOTIFY] Before Change : Change Approval = " +

args.KeepChanges.ToString( ));

}

private void OnChangedListener(object sender,

ObservableDictionaryEventArgs<TKey, TValue> args)

Trang 14

TheAddmethod callstheOnAddingmethod, which then raises the event to any ing observer objects The OnAddingmethod iscalled before the Add method on the internal dictionary iscalled After the key/value pair hasbeen added, the OnAddedmethod is called This operation is similar to the indexerset method.

listen-TheOnxxxmethodsthat raise the eventsin theObservableDictionary

class are marked asprotected virtualto allow classes to subclass this

class and implement their own method of dealing with the events

Note that thisstatement isnot applicable tosealedclasses In those

cases, you can simply make the methodspublic

The ObservableDictionaryEventArgs class contains three private fields, defined as follows:

The ObservableDictionaryObserver isthe obs erver object that watchesanyObservableDictionaryobjectsit istold about Any ObservableDictionaryobject can

be passed to the ObservableDictionaryObserver.Register method in order to be observed Thismethod acceptsan ObservableDictionary object (dictionary) asits only parameter Thismethod then hooksup the event handlersin theObservableDictionaryObserver object to the eventsthat can be rais ed by theObservableDictionaryobject passed in through thedictionaryparameter Therefore, the following events and event handlers are bound together:

• TheObservableDictionary.AddingEntry event is bound to the

ObservableDictionaryObserver.OnAddingListener event handler.

• TheObservableDictionary.AddedEntry event is bound to the

ObservableDictionaryObserver.OnAddedListener event handler.

• TheObservableDictionary.ChangingEntry event is bound to the

ObservableDictionaryObserver.OnChangingListener event handler.

• TheObservableDictionary.ChangedEntry event is bound to the

ObservableDictionaryObserver.OnChangedListener event handler.

Trang 15

Observing Additions and Modifications to Dictionaries | 341

The OnAddingListener and OnChangingListener methodswatch for additionsand changesto the key/value pairsof the watchedObservableDictionaryobject(s) Since you have an event firing before and after an addition or modification occurs, you can determine whether the addition or change should occur.

Two eventsare published by theObservableDictionaryObserverto allow for an nal entity to approve or deny the addition or changing of an entry These events are namedApproveAddandApproveChange, respectively, and are of delegate typeApproval

The ObservableDictionaryObserver object will set this flag according to whether it determinesthat the action s hould proceed or be prematurely terminated TheObservableDictionaryEventArgsobject is passed back to theOnAddingandOnChangingmethods These methods then return the value of theKeepChangesproperty to either the callingAddmethod or indexer TheAddmethod or indexer then uses this flag to determine whether the internal dictionary object should be updated.

The code in Example 9-6 shows how to instantiate ObservableDictionaries andObservableDictionaryObserversand how to register, set up approval, use, and unreg- ister them.

Example 9-6 Using the ObservableDictionary and ObservableDictionaryObserver

classes

public static void TestObserverPattern( )

{

Dictionary<int, string> dictionary1 = new Dictionary<int, string>( );

Dictionary<int, string> dictionary2 = new Dictionary<int, string>( );

Dictionary<int, string> dictionary3 = new Dictionary<int, string>( );

// Create three observable dictionary instances

var obsDict1 = dictionary1.MakeObservableDictionary( );

var obsDict2 = dictionary2.MakeObservableDictionary( );

var obsDict3 = dictionary3.MakeObservableDictionary( );

// Create an observer for the three subject objects

var observer = new ObservableDictionaryObserver<int, string>( );

// Register the three subjects with the observer

observer.Register(obsDict1);

observer.Register(obsDict2);

observer.Register(obsDict3);

Trang 16

// hook up the approval events for adding or changing

/////////////////////////////////////////////////////////////// // Create two observable SortedList instances

SortedList<string, bool> sortedList1 = new SortedList<string, bool>( ); SortedList<string, bool> sortedList2 = new SortedList<string, bool>( ); var obsSortedList1 = sortedList1.MakeObservableDictionary( );

var obsSortedList2 = sortedList2.MakeObservableDictionary( );

// Create an observer for the two subject objects

ObservableDictionaryObserver<string, bool> listObserver =

new ObservableDictionaryObserver<string, bool>( );

// Register the three subjects with the observer

// Use the observable instances

Example 9-6 Using the ObservableDictionary and ObservableDictionaryObserver classes (continued)

Trang 17

Observing Additions and Modifications to Dictionaries | 343

Note that if theObservableDictionariesare used without registering them, no events will be raised Since no events are raised, the observer cannot do its job, and values may be added to the unregistered subjects that are out of bounds for the application When using the observer design pattern in this fashion, keep in mind that fine- grained events, such as the ones in this recipe, could possibly drag down perfor- mance, so set a goal and then profile your code If you have many subjects raising many events, your application could fail to meet performance expectations.

Notice that in the second set of code exercising the ObservableDictionary, aSortedList<K,V>is used instead of aDictionary<K,V>with no difference in the usage patterns or results:

// Use Dictionary<int,string> as the base

Dictionary<int, string> dictionary1 = new Dictionary<int, string>( );

var obsDict1 = dictionary1.MakeObservableDictionary( );

// Use SortedList<string,bool> as the base

SortedList<string, bool> sortedList1 = new SortedList<string, bool>( );

var obsSortedList1 = sortedList1.MakeObservableDictionary( );

static bool SeekApproval(object sender,

ObservableDictionaryEventArgs<int, string> args)

static bool ApprovePositive(object sender,

ObservableDictionaryEventArgs<string, bool> args)

Trang 18

Lambda expressions can be implemented by the compiler from methods created by the developer There are two orthogonal characteristics that lambda expressions may have:

• Parameter lists may have explicit or implicit types.

• Bodies may be expressions or statement blocks.

Let’s start with the original way to use delegates First, you would declare a delegate type,DoWorkin this case, and then you would create an instance of it (as shown here

in the WorkItOut method) Declaring the instance of the delegate requires that you specify a method to execute when the delegate is invoked, and here theDoWorkMethodImplmethod hasbeen connected The delegate isinvoked, and the text

is written to the console via theDoWorkMethodImpl method:

class OldWay

{

// declare delegate

delegate int DoWork(string work);

// have a method to create an instance of and call the delegate

public void WorkItOut( )

Trang 19

Using Lambda Expressions | 345

class LambdaWay

{

// declare delegate

delegate int DoWork(string work);

// have a method to create an instance of and call the delegate

public void WorkItOut( )

By match we mean:

If explicitly typed, the lambda parametersmust exactly match the delegate ters If implicitly typed, the lambda parameters get the delegate parameter types The body of the lambda must be a legal expression or statement block given the parameter types.

Trang 20

parame-The return type of the lambda must be implicitly convertible to the return type of the delegate It need not match exactly.

There isyet another way you can set up the delegate, and that isthrough the magic

of delegate inference Delegate inference allows you to assign the method name directly to the delegate instance without having to write the code for creating a new delegate object Under the covers, C# actually writes the IL for creating the delegate object, but you don’t have to do it explicitly here Using delegate inference instead of writing outnew [Delegate Type]([Method Name])everywhere helpsto unclutter the code involved in the usage of delegates, as shown here:

class DirectAssignmentWay

{

// declare delegate

delegate int DoWork(string work);

// have a method to create an instance of and call the delegate

public void WorkItOut( )

Remember, the underlying delegate wrapper doesnot go away;

dele-gate inference just simplifies the syntax a bit by hiding some of it

Alternatively, you can also set up lambda expressions that take generic type ters to enable working with generic delegates as you do here in theGenericWay class:

Trang 21

Using Lambda Expressions | 347

Console.WriteLine(s);

return s;

};

// invoke string delegate

string retStr = dwString("Do some generic work");

“capturing” the variables that occurs when a lambda expression actually makes ence to one of the outer variables In the following example, thecountvariable iscap- tured and incremented by the lambda expression Thecount variable isnot part of the original scope of the lambda expression but part of the outer scope It is incre- mented and then the incremented value is returned and totaled:

refer-public void SeeOuterWork( )

Trang 22

parametersare captured, they are no longer considered to be fixed but are now able, so any unsafe code must now fix that variable before use by using the fixedkeyword.

mov-Outer variablescan affect how the compiler generatesthe internal IL for the lambda expression If it uses outer variables, it is generated as a private method of a nested class rather than as another private method of the class it is declared in, as it other- wise would be If the outer method is static, then the lambda expression cannot access instance members via the “this” keyword, as the nested class will also be gen- erated as static.

There are two types of lambda expressions: Expression lambdas and Statement lambdas This Expression lambda has no parameters and simply increments the count variable in an expression:

int count = 0;

Count countUp = ( ) => count++;

Statement lambdashave the body enclosed in curly bracesand can contain any ber of statements like this:

A few last things to remember about lambda expressions:

• They can’t usebreak,goto, orcontinueto jump from the lambda expression to a target outside the lambda expression block.

• No unsafe code can be executed inside a lambda expression.

• Lambda expressions cannot be used on the left side of theis operator.

• Since lambda expressions are a superset of anonymous methods, all restrictions that apply to anonymous methods also apply to lambda expressions.

Trang 23

Set Up Event Handlers Without the Mess | 349

with an event But as you are all fine programmers and can see the possibilities of passing data along with the event, you had to set up a delegate and an event for every event you wanted Example 9-7 demonstrates an old newspaper class that sends news to subscribers using the pre-.NET 2.0 event and event-handling methodology.

Example 9-7 Using pre-.NET 2.0 event and event-handling methods

public class IWantToKnowThen

// EventArgs derived class to hold our news data

public class NewsEventArgs : EventArgs

{

private string _latestNews;

public NewsEventArgs(string latestNews)

// Allow clients to get the news

public delegate void NewsEventHandler(Object sender, NewsEventArgs e); public event NewsEventHandler NewsEvent;

// Provide nice wrapper for sending news to clients

public void TransmitStaleNews(string news)

Trang 24

This code sets up an event that will report the news to subscribers as it comes in It passes them the news data as an argument of type NewsEventArgs that hasaLatestNews property.

As you can see from this example, whenever you had to set up multiple event dlers, it became an exercise in copy-and-paste and changing the event argument class type over and over again It would be nice to not have to define lotsof delegatesand events just to change the event arguments, as all events (and corresponding han- dlers) are supposed to look like this:

void [EventHandler](object sender, [EventArgs] args)

// Old way

public delegate void NewsEventHandler(Object sender, NewsEventArgs e);

public event NewsEventHandler NewsEvent;

// New way

public event EventHandler<NewsEventArgs> NewsEvent;

Now, you set up the nice wrapper function to allow the user to easily trigger the event:

// Old way

public void TransmitNews(string news)

{

// Copy to a temporary variable to be thread-safe

NewsEventHandler newsEvent = NewsEvent;

if (newsEvent != null)

newsEvent(this, new NewsEventArgs(news));

}

{

// Copy to a temporary variable to be thread-safe

NewsEventHandler newsEvent = NewsEvent;

Trang 25

Set Up Event Handlers Without the Mess | 351

// New way

public void TransmitNews(string news)

{

// Copy to a temporary variable to be thread-safe

EventHandler<NewsEventArgs> breakingNews = NewsEvent;

DailyPaperFlash.TransmitStaleNews("Patriots win third super bowl!"); DailyPaperFlash.TransmitStaleNews("W takes office amongst recount."); DailyPaperFlash.TransmitStaleNews("VS2005 is sooo passe");

DailyBitFlash.TransmitBreakingNews("Patriots 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);

}

}

Trang 26

The main benefit of using the genericEventHandlerinstead ofSystem.EventHandleris that you write less code Being able to declare a generic delegate allows you to have one delegate definition for multiple types You might ask: Why is this interesting? Previously, when a delegate or event was declared by a class that wanted to publish information and allow multiple client classes to subscribe to it, if any data were to be passed along to the client classes, the convention was that a new class that derived fromSystem.EventArgshad to be created Then the class would be instantiated, filled with the data, and passed to the client If the publishing class had only one event to notify people of, this wasn’t too bad If the publishing class had a lot of events, say, like a class derived from a UserControl, there would have to be a separate class derived from System.EventArgs and a separate event defined for every event that needed different data passed to it Now, with a generic delegate, you can simply declare one delegate/event for each list of parameters you deal with, and then declare the type-specific events you need Since events are supposed to have this signature:

void eventname( object sender, System.EventArgs args)

the kind folksat Micros oft gave you System.EventHandler<T> to deal with most events If your code does have events defined that have more than two parameters, there would need to be a new delegate created to be the base of those events Since most events do not have more than two parameters, this is a bit nonstandard, but not out of the question.

// Declare out delegate

delegate int DoOutWork(out string work);

Trang 27

Using Different Parameter Modifiers in Lambda Expressions | 353

// Declare ref delegate

delegate int DoRefWork(ref string work);

// Declare params delegate

delegate int DoParamsWork(params string[] workItems);

Even though the DoParamsWork delegate isdefined with the params keyword on the parameter, it can still be used as a type for a lambda expression, as you’ll see in a bit.

To use theDoOutWorkdelegate, create a lambda expression inline using the out word and assign it to theDoOutWorkdelegate instance Inside the lambda expression body, theoutvariablesis assigned a value first (as it doesn’t have one by definition asanoutparameter), writes it to the console, and returns the string hash code Note that in the parameter list, the type of s (string) must be provided, as it is not inferred for a variable marked with theoutkeyword It isnot inferred foroutorrefvariables

key-to preserve the representation at the call site and the parameter declaration site key-to help the developer clearly reason about the possible assignment to these variables:

// Declare instance and assign method

DoOutWork dow = (out string s) =>

// Declare instance and assign method

DoRefWork drw = (ref string s) =>

Trang 28

// Invoke delegate.

work = "WorkStarted";

i = drw(ref work);

Console.WriteLine(work);

While it is possible to declare a delegate with theparamsmodifier, you cannot hook

up the delegate using a lambda expression with theparamskeyword in the parameter list You get the CS1525 Invalid expression term 'params'compiler error on theDoParamsWork line:

// Done as a lambda expression you get CS1525 "Invalid expression term 'params'" DoParamsWork dpw = (params object[] workItems) =>

expres-// Done as an anonymous method you get CS1670 "params is not valid in this context" //DoParamsWork dpw = delegate(params object[] workItems)

int i = dpw("Hello", "42", "bar");

Trang 29

Using Different Parameter Modifiers in Lambda Expressions | 355

So this illustrates that you can bind a lambda expression to a delegate declared usingparams, and once you’ve done that, you can invoke the lambda expression, passing in any number of parameters you like, just as you’d expect.

Discussion

Lambda expressions cannot access thereforoutparametersof an outer scope This meansanyoutorrefvariablesthat were defined aspart of the containing method are off-limits for use inside the body of the lambda expression:

// Declare delegate

delegate int DoWork(string work);

public void TestOut(out string outStr)

// "Cannot use ref or out parameter 'outStr' inside an

// anonymous method, lambda expression, or query expression"

// "Cannot use ref or out parameter 'refStr' inside an

// anonymous method, lambda expression, or query expression"

delegate int DoWork(string work);

public void TestParams(params string[] items)

{

Trang 30

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.

Trang 31

public string Name { get; set; }

public decimal AnnualQuota { get; set; }

public decimal CommissionRate { get; set; }

public decimal Commission

delegate void CalculateEarnings(SalesWeasel weasel);

static CalculateEarnings GetEarningsCalculator(decimal quarterlySales,

decimal bonusRate)

{

Trang 32

return salesWeasel =>

{

// Figure out the weasel's quota for the quarter

decimal quarterlyQuota = (salesWeasel.AnnualQuota / 4);

// Did he make quota for the quarter?

// Check for bonus-level performance (200% of quota)

else if (quarterlySales > (quarterlyQuota * 2.0m))

To get set up, you have to create yourSalesWeasels:

// set up the sales 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=50000m, CommissionRate=0.001m }};

Then set up the earnings calculators based on quarterly earnings:

public class QuarterlyEarning

{

public string Name { get; set; }

public decimal Earnings { get; set; }

public decimal Rate { get; set; }

}

QuarterlyEarning[] quarterlyEarnings =

{ new QuarterlyEarning( ){ Name="Q1", Earnings = 65000m, Rate = 0.1m }, new QuarterlyEarning( ){ Name="Q2", Earnings = 20000m, Rate = 0.1m }, new QuarterlyEarning( ){ Name="Q3", Earnings = 37000m, Rate = 0.1m }, new QuarterlyEarning( ){ Name="Q4", Earnings = 110000m, Rate = 0.15m}};

Trang 33

static void WriteQuarterlyReport(string quarter,

static void WriteCommissionReport(decimal annualEarnings,

SalesWeasel[] weasels)

{

decimal revenueProduced = ((annualEarnings) / weasels.Length);

Console.WriteLine("");

Trang 34

Console.WriteLine("Annual Earnings were {0}",

// if his commission is more than 20%

// of what he produced, can him

Q1 Sales Earnings on Quarterly Sales of $65,000.00:

SalesWeasel Chas made a commission of : $6,900.00

SalesWeasel Ray made a commission of : $1,625.00

SalesWeasel Biff made a commission of : $70.25

Q2 Sales Earnings on Quarterly Sales of $20,000.00:

SalesWeasel Chas made a commission of : $0.00

SalesWeasel Ray made a commission of : $0.00

SalesWeasel Biff made a commission of : $20.00

Q3 Sales Earnings on Quarterly Sales of $37,000.00:

SalesWeasel Chas made a commission of : $3,700.00

SalesWeasel Ray made a commission of : $0.00

SalesWeasel Biff made a commission of : $39.45

Q4 Sales Earnings on Quarterly Sales of $110,000.00:

SalesWeasel Chas made a commission of : $12,275.00

SalesWeasel Ray made a commission of : $2,975.00

SalesWeasel Biff made a commission of : $124.63

Annual Earnings were $232,000.00

Paid Chas $22,875.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

Trang 35

Performing Multiple Operations on a List Using Functors | 361

Discussion

One of the best ways we’ve heard of to describe closures in C# is to think of an object as a set of methods associated with data and to think of a closure as a set of data associated with a function If you need to have several different operations on the same data, an object approach may make more sense These are two different angles on the same problem, and the type of problem you are solving will help you decide which isthe right approach It just dependson your inclination asto which way to go There are timeswhen one-hundred-percent pure object-oriented pro- gramming can get tedious and is unnecessary, and closures are a nice way to solve some of those problems TheSalesWeaselcommission example presented here is a demonstration of what you can do with closures It could have been done without them, but at the expense of writing more class and method code.

Closures have been defined as stated earlier, but there is a stricter definition that essentially implies that the behavior associated with the state should not be able to modify the state in order to be a true closure We tend to agree more with the first definition, as it defines what a closure should be, not how it should be implemented, which seems too restrictive Whether you choose to think of this as a neat side fea- ture of lambda expressions or you feel it is worthy of being called a closure, it is another programming trick for your toolbox and should not be dismissed.

See Also

Recipe 9.11; the “Lambda Expressions” topic in the MSDN documentation.

9.11 Performing Multiple Operations on a List Using

Functors

Problem

You want to be able to perform multiple operationson an entire collection of objects

at once, while keeping the operations functionally segmented.

Trang 36

public class StockPortfolio : IEnumerable<Stock>

#region IEnumerable<Stock> Members

public IEnumerator<Stock> GetEnumerator( )

Trang 37

Performing Multiple Operations on a List Using Functors | 363

public class Stock

{

public double GainLoss { get; set; }

public string Ticker { get; set; }

// sell the worst 3 performers

var worstPerformers = tech.GetWorstPerformers(3);

Console.WriteLine("Selling the worst performers:");

worstPerformers.DisplayStocks( );

tech.SellStocks(worstPerformers);

tech.PrintPortfolio("After Selling Worst 3 Performers");

So far, nothing terribly interesting is happening Let’s take a look at how you figured out what the three worst performers were by looking at the internals of theGetWorstPerformers method:

public IEnumerable<Stock> GetWorstPerformers(int topNumber)

in theTake extension method.

GetWorstPerformers returnsan IEnumerable<Stock>full of the three worst ers Since they aren’t making any money, you should cash in and sell them For your purposes, selling is simply removing them from the list of stocks inStockPortfolio.

perform-To accomplish this, you use yet another functor to iterate over the list of stocks handed to theSellStocksfunction (the list of worst-performing ones, in your case),

Trang 38

and then remove that stock from the internal list that the StockPortfolio class maintains:

public void SellStocks(IEnumerable<Stock> stocks)

Functorscome in a few different flavorsthat are known asa generator (a function

with no parameters), a unary function (a function with one parameter), and a binary function (a function with two parameters) If the functor happens to return a Bool- ean value, then it getsan even more special naming convention: a unary function

that returnsa Boolean iscalled a predicate, and a binary function with a Boolean return iscalled a binary predicate You will now notice in the Framework that there

are both Predicate<T> and BinaryPredicate<T> delegatesdefined to facilitate these uses of functors.

The List<T> and System.Array classes take predicates (Predicate<T>,BinaryPredicate<T>), actions(Action<T>), comparisons (Comparison<T>), and convert- ers(Converter<T,U>) Thisallowsthese collectionsto be operated on in a much more general way than was previously possible.

Thinking in termsof functorscan be a bit of a challenge at first, but once you put a bit of time into it, you can start to see powerful possibilities open up before you Any code you can write once, debug once, and use many times is a useful thing, and func- tors can help you get to that place.

The output for the example is listed here:

Trang 39

Performing Multiple Operations on a List Using Functors | 365

After Selling Worst 3 Performers

The “System.Collections.Generic.List<T>,” “System.Linq.Enumerable Class,” and

“System.Array” topics in the MSDN documentation.

Trang 40

Regular expressions take the form of a pattern that can be matched to zero or more characters within a string The simplest of these patterns, such as * (match anything except newline characters) and [A-Za-z] (match any letter) are easy to learn, but more advanced patternscan be difficult to learn and even more difficult to imple- ment correctly Learning and understanding regular expressions can take consider- able time and effort, but the work will pay off.

Regular expression patterns can take a simple form—such as a single word or acter—or a much more complex pattern The more complex patternscan recognize and match such thingsasthe year portion of a date, all of the<SCRIPT>tagsin an ASP page, or a phrase in a sentence that varies with each use The NET regular expres- sion classes provide a very flexible and powerful way to do such things as recognize text, replace text within a string, and split up text into individual sections based on one or more complex delimiters.

char-Despite the complexity of regular expression patterns, the regular expression classes

in the FCL are easy to use in your applications Executing a regular expression sists of the following steps:

con-1 Create an instance of aRegexobject that contains the regular expression pattern along with any options for executing that pattern.

2 Retrieve a reference to an instance of aMatchobject by calling theMatchinstance method if you want only the first match found Or, retrieve a reference to an instance of theMatchesCollectionobject by calling theMatchesinstance method

if you want more than just the first match found If, however, you want to know

Ngày đăng: 12/08/2014, 09:22

TỪ KHÓA LIÊN QUAN