Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

}

}

public class Test

{

public delegate void SampleDelegate();

public void Run()

{

SampleDelegate delegateInstance = this.Target2; delegateInstance += this.Target1;

try

{

delegateInstance.SafeInvoke();

}

catch(AggregateException ex)

{

// Do any exception handling here

}

}

private void Target1()

{

Console.WriteLine("Target 1 executed");

}

private void Target2()

{

Console.WriteLine("Target 2 executed"); throw new Exception();

}

}

This outputs:

Target 2 executed

Target 1 executed

Invoking directly, without SaveInvoke, would only execute Target 2.

Section 96.5: Delegate Equality

Calling .Equals() on a delegate compares by reference equality:

Action action1 = () => Console.WriteLine("Hello delegates");

Action action2 = () => Console.WriteLine("Hello delegates");

Action action1Again = action1;

Console.WriteLine(action1.Equals(action1)) // True

Console.WriteLine(action1.Equals(action2)) // False

Console.WriteLine(action1Again.Equals(action1)) // True

These rules also apply when doing += or -= on a multicast delegate, for example when subscribing and unsubscribing from events.

Section 96.6: Underlying references of named method

GoalKicker.com – C# Notes for Professionals

525

delegates

When assigning named methods to delegates, they will refer to the same underlying object if:

They are the same instance method, on the same instance of a class

They are the same static method on a class

public class Greeter

{

public void WriteInstance()

{

Console.WriteLine("Instance");

}

public static void WriteStatic()

{

Console.WriteLine("Static");

}

}

// ...

Greeter greeter1 = new Greeter();

Greeter greeter2 = new Greeter();

Action instance1 = greeter1.WriteInstance;

Action instance2 = greeter2.WriteInstance;

Action instance1Again = greeter1.WriteInstance;

Console.WriteLine(instance1.Equals(instance2)); // False

Console.WriteLine(instance1.Equals(instance1Again)); // True

Action @static = Greeter.WriteStatic;

Action staticAgain = Greeter.WriteStatic;

Console.WriteLine(@static.Equals(staticAgain)); // True

Section 96.7: Assigning a named method to a delegate

Named methods can be assigned to delegates with matching signatures:

public static class Example

{

public static int AddOne(int input)

{

return input + 1;

}

}

Func<int,int> addOne = Example.AddOne

Example.AddOne takes an int and returns an int, its signature matches the delegate Func<int,int>.

Example.AddOne can be directly assigned to addOne because they have matching signatures.

GoalKicker.com – C# Notes for Professionals

526

Section 96.8: Assigning to a delegate by lambda

Lambdas can be used to create anonymous methods to assign to a delegate:

Func<int,int> addOne = x => x+1;

Note that the explicit declaration of type is required when creating a variable this way:

var addOne = x => x+1; // Does not work

Section 96.9: Encapsulating transformations in funcs

public class MyObject{

public DateTime? TestDate { get; set; }

public Func<MyObject, bool> DateIsValid = myObject => myObject.TestDate.HasValue && myObject.TestDate > DateTime.Now;

public void DoSomething(){

//We can do this:

if(this.TestDate.HasValue && this.TestDate > DateTime.Now){ CallAnotherMethod();

}

//or this: if(DateIsValid(this)){

CallAnotherMethod();

}

}

}

In the spirit of clean coding, encapsulating checks and transformations like the one above as a Func can make your code easier to read and understand. While the above example is very simple, what if there were multiple DateTime properties each with their own di ering validation rules and we wanted to check di erent combinations? Simple, one-line Funcs that each have established return logic can be both readable and reduce the apparent complexity of your code. Consider the below Func calls and imagine how much more code would be cluttering up the method:

public void CheckForIntegrity(){

if(ShipDateIsValid(this) && TestResultsHaveBeenIssued(this) && !TestResultsFail(this)){ SendPassingTestNotification();

}

}

Section 96.10: Passing delegates as parameters

Delegates can be used as typed function pointers:

class FuncAsParameters

{

public void Run()

{

DoSomething(ErrorHandler1); DoSomething(ErrorHandler2);

}

public bool ErrorHandler1(string message)

{

GoalKicker.com – C# Notes for Professionals

527

Console.WriteLine(message); var shouldWeContinue = ...

return shouldWeContinue;

}

public bool ErrorHandler2(string message)

{

// ...Write message to file...

var shouldWeContinue = ...

return shouldWeContinue;

}

public void DoSomething(Func<string, bool> errorHandler)

{

// In here, we don't care what handler we got passed!

...

if (...error...)

{

if (!errorHandler("Some error occurred!"))

{

// The handler decided we can't continue return;

}

}

}

}

Section 96.11: Closure inside a delegate

Closures are inline anonymous methods that have the ability to use Parent method variables and other anonymous methods which are defined in the parent's scope.

In essence, a closure is a block of code which can be executed at a later time, but which maintains the environment in which it was first created - i.e. it can still use the local variables etc of the method which created it, even after that method has finished executing. -- Jon Skeet

delegate int testDel();

static void Main(string[] args)

{

int foo = 4;

testDel myClosure = delegate()

{

return foo;

};

int bar = myClosure();

}

Example taken from Closures in .NET.

GoalKicker.com – C# Notes for Professionals

528