Я пытаюсь разобраться с делегатами на С#, но я просто не понимаю, как их использовать. Вот несколько слегка реконструированный код на странице MSDN на делегатах:
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 Clues for the Clueless",
"Scott Adams", 12.00m, true);
}
}
}
Как вы можете видеть в классе BookDB
, я определил 2 разных метода:
- Тот, который принимает делегат в качестве аргумента:
ProcessPaperbackBooksWithDelegate
- Тот, который принимает действие соответствующей сигнатуры типа в качестве аргумента:
ProcessPaperbackBooksWithoutDelegate
Вызов одного из них возвращает тот же результат; поэтому какая цель решает делегат?
Второй пример на той же странице приводит к большей путанице; вот код:
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
- это функция, которая уже была в этом примере. Main2
и Main3
добавлены мной скрипты.
Как я и ожидал, Main1
и Main2
дают тот же результат, 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
, однако, дает очень странный результат:
Invoking function a: Hello, A!
Invoking function b: Goodbye, B!
Invoking function c: Goodbye, C!
Invoking function d: Goodbye, D!
Если +
фактически выполнял композицию функций, тогда результат (для Main3
) должен был быть:
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.
Но ясно, что +
на самом деле не является традиционным функциональным составом (реальный состав, по-моему, даже не работал бы на Action). Это многое видно из того факта, что он, похоже, не имеет сигнатуры типа:
(T2 -> T3) -> (T1 -> T2) -> T1 -> T3
вместо этого подпись типа выглядит следующим образом:
(T1 -> T2) -> (T1 -> T2) -> (T1 -> T2)
Итак, что означают +
и -
?
Кроме того: Я пытался использовать var a = Hello;...
в Main2
, но получил ошибку:
test.cs(136,14): error CS0815: Cannot assign method group to an implicitly-typed
local variable
Возможно, это не связано с этим вопросом, но почему он не может этого сделать? Это похоже на довольно простой вывод типа.