I have been trying to get my head around delegates in C#, but I just don't seem to get the point of using them. Here is some slightly reconstructed code from the MSDN page on delegates:
using System;
using System.Collections;
namespace Delegates
{
// 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 ProcessPaperbackBooksWithDelegate(ProcessBookDelegate processBook)
{
foreach (Book b in list)
{
if (b.Paperback)
processBook(b);
}
}
public void ProcessPaperbackBooksWithoutDelegate(Action<Book> action)
{
foreach (Book b in list)
{
if (b.Paperback)
action(b);
}
}
}
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();
AddBooks(bookDB);
Console.WriteLine("Paperback Book Titles Using Delegates:");
bookDB.ProcessPaperbackBooksWithDelegate(new ProcessBookDelegate(PrintTitle));
Console.WriteLine("Paperback Book Titles Without Delegates:");
bookDB.ProcessPaperbackBooksWithoutDelegate(PrintTitle);
}
// 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);
}
}
}
As you can see in the BookDB
class, I have defined 2 different methods:
- One which takes a delegate as an argument:
ProcessPaperbackBooksWithDelegate
- One which takes an action of the corresponding type signature as argument:
ProcessPaperbackBooksWithoutDelegate
A call to either of them returns the same result; so what purpose does a delegate solve?
The second example on the same page leads to lot more confusion; here is the code:
delegate void MyDelegate(string s);
static 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 string HelloS(string s)
{
return string.Format("Hello, {0}!", s);
}
public static string GoodbyeS(string s)
{
return string.Format("Goodbye, {0}!", s);
}
public static void Main1()
{
MyDelegate a, b, c, d;
a = new MyDelegate(Hello);
b = new MyDelegate(Goodbye);
c = a + b;
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");
}
public static void Main2()
{
Action<string> a = Hello;
Action<string> b = Goodbye;
Action<string> c = a + b;
Action<string> 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");
}
public static void Main3()
{
Func<string, string> a = HelloS;
Func<string, string> b = GoodbyeS;
Func<string, string> c = a + b;
Func<string, string> d = c - a;
Console.WriteLine("Invoking function a: " + a("A"));
Console.WriteLine("Invoking function b: " + b("B"));
Console.WriteLine("Invoking function c: " + c("C"));
Console.WriteLine("Invoking function d: " + d("D"));
}
}
Main1
is the function which was already in the example. Main2
and Main3
are fiddles added by me.
As I expected, Main1
and Main2
give the same result i.e.:
Invoking delegate a:
Hello, A!
Invoking delegate b:
Goodbye, B!
Invoking delegate c:
Hello, C!
Goodbye, C!
Invoking delegate d:
Goodbye, D!
Main3
however, gives a very strange result:
Invoking function a: Hello, A!
Invoking function b: Goodbye, B!
Invoking function c: Goodbye, C!
Invoking function d: Goodbye, D!
If +
was actually performing function composition then the result(for Main3
) should have been:
Invoking function a: Hello, A!
Invoking function b: Goodbye, B!
Invoking function c: Hello, Goodbye, C!!
Invoking function d: //God knows what this should have been.
But it is clear that +
isn't actually the traditional functional composition(real composition wouldn't even work for an Action, I guess). That much is evident from the fact that it doesn't seem to have a type signature of:
(T2 -> T3) -> (T1 -> T2) -> T1 -> T3
instead, the type signature seems to be:
(T1 -> T2) -> (T1 -> T2) -> (T1 -> T2)
So what do +
and -
really mean?
Aside: I tried to use var a = Hello;...
in Main2
but got the error:
test.cs(136,14): error CS0815: Cannot assign method group to an implicitly-typed
local variable
It may not be related to this question, but why can't it do so? It seems like a pretty straight forward type deduction.