Jon Shemitz Complicated stuff, quickly.

Post on 11-Feb-2016

38 views 0 download

Tags:

description

Advanced Delegates. Jon Shemitz Complicated stuff, quickly. Advanced Delegates. Delegate syntax Delegate Arcana40% Invocation lists, events, dynamic delegates Delegates vs interfaces Miscellaneous 2.0 enhancements Anonymous Methods15% Delegates as t hread primitives - PowerPoint PPT Presentation

Transcript of Jon Shemitz Complicated stuff, quickly.

Jon ShemitzComplicated stuff, quickly.

AdvancedDelegates

Advanced Delegates

Delegate syntax● Delegate Arcana 40%

Invocation lists, events, dynamic delegatesDelegates vs interfacesMiscellaneous 2.0 enhancements

● Anonymous Methods 15%

Delegates as thread primitives● Asynchronous Invoke 30%● The System Thread Pool 15%

Invocation Lists

● Delegate class manages an invocation listImplementation changed from 1.1 to 2.0Calling (invoking) a delegate is much faster in 2.0

● Each invocation list is immutableOperators like + and - return new lists

The static methods Delegate.Combine and Delegate.Remove

+= and -= work by replacement

Delegate Combining

● Value equality, not reference equalityYou do not have to -= the delegate instance you added

Can += Instance.Method now and -= Instance.Method laterYes, these are two separate delegate instances

● List length matters, on Delegate.RemoveSubtract Two from Two, and you have ZeroSubtract Four from Two, and you have Two

● These rules apply to events, too

Event Syntax

Partial understanding of events is nearly universal!● Simple, field-like events have implicit methods.

An event is never a local variable.Within its class, an event is a delegate -

Outside, an event is the implicit add and remove methods.● Complex, property-like events have no fields.

Explicit add and remove methods.Only those two methods, even within class - not storage.

Create Delegate

Delegate.CreateDelegatePass a MethodInfo, or a method name

Fewer constraints in 2.0

Calling the delegate is faster than MethodInfo.InvokeGood with dynamically generated methods

Getting dynamically loaded code by name is badSubject to collisionsInterface gets code by contract

(Code By Contract)

Standard plug-in architecture:● Contract library, used by app and plug-ins● Contains your IPlugin interface● Loading plug-in does GetExportedTypes

Finds types assignment compatible with IPlugin Creates instance, and casts to IPlugin

Delegate Enhancements in 2.0● New method group syntax

DelegateType DelegateInstance = Instance.Method;Don't have to say new DelegateType(Instance.Method)Just like Delphi ...

Instance.Method specifies an overload method groupValid delegate if one and only one overload in group

● SpeedInterface was 2½ times faster than delegate in 1.xDelegates are ca 5% faster than interfaces in 2.x

Choose on Semantics

Interface advantagesContracts & dynamically loaded code.● Interfaces are free from versioning

issues and name collisions.● You can control which methods

implement the interface.● An interface reference can give you

access to a whole personality:Other methods in interface, and other interfaces on object.

● Interfaces used to be faster than delegates. Same speed, in 2.0.

Delegate advantagesCallbacks & thread procs.● Less code to create a delegate than

to support an interface - especially when interface supported by a nested class.

● Class may offer a choice of delegate compatible methods; interface reference means the class must explicitly support the interface.

● You can create a delegate to a static or anonymous method. Interface methods are always named instance methods.

Covarianceclass Base{ public static Derived BaseFn(Base B) { return new Derived(); }}class Derived : Base {}delegate Base BaseFunction(Base B);

●In 1.x, can’t say BaseFunction F = new BaseFunction(Base.BaseFn)

A BaseFunction returns a Base instance.Base.BaseFn returns a Derived instance.

●Can, in 2.xEvery Derived instance is also a Base instance.

Contravariance class Base { public static void BaseProc(Base B) { } } class Derived : Base { public static void DerivedProc(Derived D) { }

} delegate void BaseMethod(Base B); delegate void DerivedMethod(Derived D);

●A method that takes a base type is compatible with a delegate that takes a derived type:

DerivedMethod BaseD = new DerivedMethod(Base.BaseProc);

Complicated Names

● Practical effect is that matching is looser● You'll probably only notice covariance and

contravariance when 2.0 code won't compile under 1.x, “even though it's not using generics”

● Don't put effort into telling them apart, or even remembering details of how matching is looserJust remember assignment compatibility

That's it for the basics

Any questions?

Advanced DelegatesAnonymous MethodsAsynchronous calls

Delegates are asynch primitives

When a method isn't right

Is there something wrong with named methods?Until 2.0, delegates always referred to named methods.

Usually fine - but there are three problems:● Callbacks are hard to read.

They make you jump around. They add clutter and bloat.● No tie between callback and caller.● Boilerplate - copying local variables to tiny state

objects to support callbacks &c.

Anonymous Methods● Defining a method just so that you can create a delegate to it

makes your code harder to write and harder to read.● Part of a method has been hoisted into another method.

● C# 2.0 supports anonymous methods, which let you create a delegate to a special statement block, within a method.

● The compiler does the hoisting.● You can't call an anonymous method by mistake.● The compiler does capture better than you do.

(The next screen is about capture.)

Capture● Anonymous methods can capture parameters and/or local

variables.● Captured variables are implemented as fields in a singleton

instance of a Compiler Generated Class. Captured parameters are copied in, by method prologue,

then only referred to as CGC fields.● Captured variables last as long as the delegate lasts.● Anonymous methods are named methods of the CGC.

Anonymous Method Syntax

● delegate (optional parametersparameters) {}An anonymous method never has a return type

● Valid in delegate expressions:In assignment;

DelegateType DelegateInstance = delegate(int N) {}; // note the ; after the }

As a parameter to a method; Fn(delegate {})

Event list editing.SomeEvent += delegate(object sender, EventArgs E) {};

Asynchronous Calls

● You can run any delegate in a thread Available in 1.0 Most people don't know this Syntax is weird, so people “pass” and never return You have to add two null parameters to BeginInvoke

● Functional programmingEndInvoke is like Join, except it returns a typed result.Delegates are asynch primitives:

Easiest way to pass parameters & get thread results.

Asynchronous Invoke

The best way to pass data to/from a threadFor example: read file to string

Start a read runningDo any other setupEndInvoke when really need resultA cheap, functional thread

Takes a stringReturns a string

May use ThreadPool directly if don't need result.

Runtime Methods

All delegates have runtime methods● Synchronous Invoke, in caller's thread● Asynchronous BeginInvoke, in ThreadPool thread

Returns IAsyncResult● EndInvoke takes that IAsyncResult

IAsyncResult contains a wait handleMust always call EndInvoke

EndInvoke

● BeginInvoke starts asynch call● Collect results with .EndInvoke()

Returns same type as delegateWhether void or string or whatever

Can use asynch invoke with void delegate to pass parameters -Cheaper than creating anonymous method that captures parameters.

No casting!● Less overhead than creating new Thread

Less OS work, and less user code

Control.BeginInvoke

● Two different BeginInvoke methodsBoth return IAsyncResultNever confuse the two!

● Delegate BeginInvoke runs in own threadMust call EndInvoke

● Control.BeginInvoke runs in control's threadCan omit call to EndInvoke

A Parallelizing Example//foreach (T Datum in Data)// P(Datum); // Process each Datum in the current thread

public class ThreadEach<T>{ public delegate void Processor(T Datum);

public static void Process(Processor P, IEnumerable<T> Data) { List<IAsyncResult> AsyncResults = new List<IAsyncResult>();

// Process each Datum in its own thread foreach (T Datum in Data) AsyncResults.Add(P.BeginInvoke(Datum, null, null));

// Wait for all threads to finish foreach (IAsyncResult Async in AsyncResults) P.EndInvoke(Async); }}

That was a bad example

What was wrong with that?● Not a good template

Each delegate has own parameter list● Too many threads!

Need to match thread count to processor countUse a Semaphore so only one thread per processorEach thread signs in and signs out

Those Two Nulls

● The 'extra' parameters to BeginInvokeDon't have to be null

● A delegate called after asynch delegate returnsIn same ThreadPool thread

● A parameter to the callback delegateHard to see point of this - butCan be used for fire and forget

Explicit ThreadPool is better for this

The system ThreadPool

A high level of control When you manually create a thread

Priority, IsBackground, etc

You don’t need that level of control in every app

Creating a thread is cheaper than creating a processBut it’s not free – there are setup and teardown costs

The ThreadPool reuses threads

Thread reuse

When you explicitly create a Thread:Thread executes the delegate passed to constructorThread dies when delegate returnsThread is a foreground thread, by default

A TheadPool thread:A background threadDelegate to method of object with delegate field

Executes stored delegateOn return, add the thread to a ready list A new stored delegate when wait handle signals

Cheaper and Less Code

Defers Thread destruction until process terminationMore importantly:Reusing threads reduces threads a process creates

ThreadPool takes less code, tooJust pass a delegate to a method, and it executesDon't have to Start a Thread

Explicit ThreadPool

ThreadPool.QueueUserWorkItem static method Executes a WaitCallback delegate

delegate void WaitCallback (object state)

Only one parameter, and it's untypedAnonymous methods can capture state information

(example on the next slide)

Explicit Exampleprivate static void CaptureExample()

{

string Report = @"Some very big string";

string Filename = @"\A\Drive\Near\You\Report.txt";

ThreadPool.QueueUserWorkItem(delegate {

WriteFile(Filename, Report);

} );

}

Captures Filename and Report from containing method.

Wait callbacks

The WaitCallback delegate is strangely namedIt's often executed straight-away

The ThreadPool has a different sort of wait callbackThreadPool.RegisterWaitForSingleObject

Takes a WaitOrTimerCallback delegate and a wait handle

More efficient, less reliableOne blocked thread, instead of manyMay want to compile a blocking script

Thank you

Any Questions?

Midnight Beach

ContractingConsultingTraining

Jon Shemitz

Talks fast.

Codes fast, too.