0% found this document useful (0 votes)
56 views

Sample Files: Delegates Tutorial

This document provides a tutorial on delegates in C#. It demonstrates how to declare delegate types to reference static and instance methods, and how to combine (multicast) delegates. The tutorial includes two examples, one showing how to declare, instantiate, and call a delegate, and another demonstrating how to compose two delegates using the + and - operators. Delegates allow encapsulating method references to provide separation of functionality and enable "anonymous" method invocation without compile-time knowledge of which method will be called.
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
56 views

Sample Files: Delegates Tutorial

This document provides a tutorial on delegates in C#. It demonstrates how to declare delegate types to reference static and instance methods, and how to combine (multicast) delegates. The tutorial includes two examples, one showing how to declare, instantiate, and call a delegate, and another demonstrating how to compose two delegates using the + and - operators. Delegates allow encapsulating method references to provide separation of functionality and enable "anonymous" method invocation without compile-time knowledge of which method will be called.
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

Delegates Tutorial

This tutorial demonstrates the delegate types. It shows how to map


delegates to static and instance methods, and how to combine them
(multicast).

Sample Files
See Delegates Sample to download and build the sample files
discussed in this tutorial.

Further Reading
● delegate
● 15. Delegates
● Events Tutorial
● Asynchronous Delegates

Tutorial
A delegate in C# is similar to a function pointer in C or C++. Using
a delegate allows the programmer to encapsulate a reference to a
method inside a delegate object. The delegate object can then be
passed to code which can call the referenced method, without having
to know at compile time which method will be invoked. Unlike function
pointers in C or C++, delegates are object-oriented, type-safe, and
secure.
A delegate declaration defines a type that encapsulates a method with
a particular set of arguments and return type. For static methods, a
delegate object encapsulates the method to be called. For instance
methods, a delegate object encapsulates both an instance and
a method on the instance. If you have a delegate object and an
appropriate set of arguments, you can invoke the delegate with the
arguments.
An interesting and useful property of a delegate is that it does not
know or care about the class of the object that it references. Any
object will do; all that matters is that the method's argument types
and return type match the delegate's. This makes delegates perfectly
suited for "anonymous" invocation.
Note Delegates run under the caller's security permissions, not the
declarer's permissions.
This tutorial includes two examples:
● Example 1 shows how to declare, instantiate, and call a delegate.
● Example 2 shows how to combine two delegates.
In addition, it discusses the following topics:
● Delegates and Events
● Delegates vs. Interfaces

Example 1
The following example illustrates declaring, instantiating, and
using a delegate. The BookDB class encapsulates a bookstore
database that maintains a database of books. It exposes a method
ProcessPaperbackBooks, which finds all paperback books in the
database and calls a delegate for each one. The delegate type used is
called ProcessBookDelegate. The Test class uses this class to print out
the titles and average price of the paperback books.
The use of delegates promotes good separation of functionality
between the bookstore database and the client code. The client code
has no knowledge of how the books are stored or how the bookstore
code finds paperback books. The bookstore code has no knowledge of
what processing is done on the paperback books after it finds them.
Copy
// bookstore.cs
using System;

// A set of classes for handling a bookstore:


namespace Bookstore
{
using System.Collections;

// Describes a book in the book list:


public struct Book
{
public string Title; // Title of the book.
public string Author; // Author of the book.
public decimal Price; // Price of the book.
public bool Paperback; // Is it paperback?

public Book(string title, string author, decimal price, bool


paperBack)
{
Title = title;
Author = author;
Price = price;
Paperback = paperBack;
}
}

// Declare a delegate type for processing a book:


public delegate void ProcessBookDelegate(Book book);

// Maintains a book database.


public class BookDB
{
// List of all books in the database:
ArrayList list = new ArrayList();

// Add a book to the database:


public void AddBook(string title, string author, decimal price, bool
paperBack)
{
list.Add(new Book(title, author, price, paperBack));
}

// Call a passed-in delegate on each paperback book to process it:


public void ProcessPaperbackBooks(ProcessBookDelegate
processBook)
{
foreach (Book b in list)
{
if (b.Paperback)
// Calling the delegate:
processBook(b);
}
}
}
}

// Using the Bookstore classes:


namespace BookTestClient
{
using Bookstore;

// Class to total and average prices of books:


class PriceTotaller
{
int countBooks = 0;
decimal priceBooks = 0.0m;

internal void AddBookToTotal(Book book)


{
countBooks += 1;
priceBooks += book.Price;
}

internal decimal AveragePrice()


{
return priceBooks / countBooks;
}
}

// Class to test the book database:


class Test
{
// Print the title of the book.
static void PrintTitle(Book b)
{
Console.WriteLine(" {0}", b.Title);
}

// Execution starts here.


static void Main()
{
BookDB bookDB = new BookDB();

// Initialize the database with some books:


AddBooks(bookDB);

// Print all the titles of paperbacks:


Console.WriteLine("Paperback Book Titles:");
// Create a new delegate object associated with the static
// method Test.PrintTitle:
bookDB.ProcessPaperbackBooks(new
ProcessBookDelegate(PrintTitle));

// Get the average price of a paperback by using


// a PriceTotaller object:
PriceTotaller totaller = new PriceTotaller();
// Create a new delegate object associated with the nonstatic
// method AddBookToTotal on the object totaller:
bookDB.ProcessPaperbackBooks(new
ProcessBookDelegate(totaller.AddBookToTotal));
Console.WriteLine("Average Paperback Book Price: ${0:#.##}",
totaller.AveragePrice());
}

// Initialize the book database with some test books:


static void AddBooks(BookDB bookDB)
{
bookDB.AddBook("The C Programming Language",
"Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true);
bookDB.AddBook("The Unicode Standard 2.0",
"The Unicode Consortium", 39.95m, true);
bookDB.AddBook("The MS-DOS Encyclopedia",
"Ray Duncan", 129.95m, false);
bookDB.AddBook("Dogbert's Clues for the Clueless",
"Scott Adams", 12.00m, true);
}
}
}

Output
Copy
Paperback Book Titles:
The C Programming Language
The Unicode Standard 2.0
Dogbert's Clues for the Clueless
Average Paperback Book Price: $23.97

Code Discussion
● Declaring a delegate The following statement:
●Copy
●public delegate void ProcessBookDelegate(Book book);

●declares a new delegate type. Each delegate type describes the


number and types of the arguments, and the type of the return
value of methods that it can encapsulate. Whenever a new set of
argument types or return value type is needed, a new delegate
type must be declared.
●Instantiating a delegate Once a delegate type has been
declared, a delegate object must be created and associated with
a particular method. Like all other objects, a new delegate object
is created with a new expression. When creating a delegate,
however, the argument passed to the new expression is special —
it is written like a method call, but without the arguments to the
method.
●The following statement:
●Copy
●bookDB.ProcessPaperbackBooks(new
ProcessBookDelegate(PrintTitle));
●creates a new delegate object associated with the static method
Test.PrintTitle. The following statement:
●Copy
●bookDB.ProcessPaperbackBooks(new
ProcessBookDelegate(totaller.AddBookToTotal));

●creates a new delegate object associated with the nonstatic


method AddBookToTotal on the object totaller. In both
cases, this new delegate object is immediately passed to the
ProcessPaperbackBooks method.
●Note that once a delegate is created, the method it is associated
with never changes — delegate objects are immutable.
●Calling a delegate Once a delegate object is created, the
delegate object is typically passed to other code that will call the
delegate. A delegate object is called by using the name of the
delegate object, followed by the parenthesized arguments to be
passed to the delegate. An example of a delegate call is:
●Copy
●processBook(b);

●A delegate can either be called synchronously, as in this example,


or asynchronously by using BeginInvoke and EndInvoke methods.

Example 2
This example demonstrates composing delegates. A useful property of
delegate objects is that they can be composed using the "+" operator.
A composed delegate calls the two delegates it was composed from.
Only delegates of the same type can be composed.
The "-" operator can be used to remove a component delegate from a
composed delegate.
Copy
// compose.cs
using System;

delegate void MyDelegate(string s);

class MyClass
{
public static void Hello(string s)
{
Console.WriteLine(" Hello, {0}!", s);
}

public static void Goodbye(string s)


{
Console.WriteLine(" Goodbye, {0}!", s);
}

public static void Main()


{
MyDelegate a, b, c, d;

// Create the delegate object a that references


// the method Hello:
a = new MyDelegate(Hello);
// Create the delegate object b that references
// the method Goodbye:
b = new MyDelegate(Goodbye);
// The two delegates, a and b, are composed to form c:
c = a + b;
// Remove a from the composed delegate, leaving d,
// which calls only the method Goodbye:
d = c - a;

Console.WriteLine("Invoking delegate a:");


a("A");
Console.WriteLine("Invoking delegate b:");
b("B");
Console.WriteLine("Invoking delegate c:");
c("C");
Console.WriteLine("Invoking delegate d:");
d("D");
}
}
Output
Copy
Invoking delegate a:
Hello, A!
Invoking delegate b:
Goodbye, B!
Invoking delegate c:
Hello, C!
Goodbye, C!
Invoking delegate d:
Goodbye, D!

Delegates and Events


Delegates are ideally suited for use as events — notifications from
one component to "listeners" about changes in that component. For
more information on the use of delegates for events, see the Events
Tutorial.

Delegates vs. Interfaces


Delegates and interfaces are similar in that they enable the separation
of specification and implementation. Multiple independent authors
can produce implementations that are compatible with an interface
specification. Similarly, a delegate specifies the signature of a method,
and authors can write methods that are compatible with the delegate
specification. When should you use interfaces, and when should you
use delegates?
Delegates are useful when:
● A single method is being called.
● A class may want to have multiple implementations of the
method specification.
● It is desirable to allow using a static method to implement the
specification.
● An event-like design pattern is desired (for more information, see
the Events Tutorial).
● The caller has no need to know or obtain the object that the
method is defined on.
● The provider of the implementation wants to "hand out"
the implementation of the specification to only a few select
components.
● Easy composition is desired.
Interfaces are useful when:
● The specification defines a set of related methods that will be
called.
● A class typically implements the specification only once.
● The caller of the interface wants to cast to or from the interface
type to obtain other interfaces or classes.

You might also like