Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用委托和使用Func<;T>/行动<;T>;在方法签名中?_C#_Delegates - Fatal编程技术网

C# 使用委托和使用Func<;T>/行动<;T>;在方法签名中?

C# 使用委托和使用Func<;T>/行动<;T>;在方法签名中?,c#,delegates,C#,Delegates,我一直试图用C#来说服代表们,但我似乎不明白使用它们的意义。下面是一些从“代理”页面中略作重构的代码: using System; using System.Collections; namespace Delegates { // Describes a book in the book list: public struct Book { public string Title; // Title of the book.

我一直试图用C#来说服代表们,但我似乎不明白使用它们的意义。下面是一些从“代理”页面中略作重构的代码:

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);
        }
    }
}
Main1
是示例中已有的函数
Main2
Main3
是我添加的小提琴

正如我所料,
Main1
Main2
给出了相同的结果,即:

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.
但是很明显,
+
实际上并不是传统的功能组合(我想,真正的组合甚至对动作都不起作用)。这一点很明显,因为它似乎没有以下类型的签名:

(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

它可能与这个问题无关,但为什么它不能这样做呢?这似乎是一个非常直接的类型推断。

自定义委托类型与
Func
Action
当您可以通过
委托获得相同的结果时,为什么要使用
Func
和/或
Action
? 因为:

  • 它省去了为每个可能的方法签名创建自定义委托类型的麻烦。在代码中,少就是多
  • 不同的自定义委托类型不兼容,即使它们的签名完全匹配。你可以解决这个问题,但它是冗长的

  • 自从引入
    Func
    Action
    以来,这就是编写代码的惯用方法。除非有令人信服的相反理由,否则你要像罗马人那样做
让我们看看问题是什么:

// Delegates: same signature but different types
public delegate void Foo();
public delegate void Bar();

// Consumer function -- note it accepts a Foo
public void Consumer(Foo f) {}
试一试:

Consumer(new Foo(delegate() {})); // works fine
Consumer(new Bar(delegate() {})); // error: cannot convert "Bar" to "Foo"
最后一行是有问题的:没有技术原因说明它不能工作,但是编译器将
Foo
Bar
视为不同的类型,并且不允许它。这会导致摩擦,因为如果你只有一个
,你就必须写

var bar = new Bar(delegate() {});
Consumer(new Foo(bar)); // OK, but the ritual isn't a positive experience
为什么在
Func
和/或
Action
上使用委托? 因为:

  • 您的目标是C#的早期版本,其中不存在这些类型
  • 您正在处理复杂的函数签名。没有人想多次键入此内容:
    Func

因为我认为这两种情况都是罕见的,在日常使用中,实际的答案是“根本没有理由”。 组合多播代理 C#中的所有委托都是多播委托——也就是说,调用它们可能会调用具有该签名的任意数量的方法。操作员

+
-
不执行函数合成;它们从多播委托中添加和删除委托。例如:

void Foo() {}
void Bar() {}

var a = new Action(Foo) + Bar;
a(); // calls both Foo() and Bar()
您可以使用
操作符-
从多播委托中删除委托,但必须传入完全相同的委托。如果右侧操作数不是多播委托的一部分,则不会发生任何事情。例如:

var a = new Action(Foo);
a();      // calls Foo()
a -= Bar; // Bar is not a part of the multicast delegate; nothing happens
a();      // still calls Foo() as before
public int Ret1() { return 1; }
public int Ret2() { return 2; }

Console.WriteLine((new Func<int>(Ret1) + Ret2)()); // prints "2"
Console.WriteLine((new Func<int>(Ret2) + Ret1)()); // prints "1"
多播委托返回值 使用非
void
返回类型调用多播委托会导致该多播委托最后添加的成员返回的值。例如:

var a = new Action(Foo);
a();      // calls Foo()
a -= Bar; // Bar is not a part of the multicast delegate; nothing happens
a();      // still calls Foo() as before
public int Ret1() { return 1; }
public int Ret2() { return 2; }

Console.WriteLine((new Func<int>(Ret1) + Ret2)()); // prints "2"
Console.WriteLine((new Func<int>(Ret2) + Ret1)()); // prints "1"
当token
isinterest
出现在源代码中时,它是一个方法组(请注意,方法组当然可以由一个方法组成,如您的示例所示)

由于您没有尝试将编译时错误转换为兼容的委托类型,因此预期会出现编译时错误(规范强制执行该错误)。更明确地解决问题:

// both of these convert the method group to the "obviously correct" delegate
Func<int, bool> f1 = IsInteresting;
Func<string, bool> f2 = IsInteresting;
//这两者都将方法组转换为“显然正确”的委托
Func f1=正在进行测试;
Func f2=正在进行测试;
用外行的话说,编写
var f=isinterest
没有意义,因为编译器唯一合理的做法是创建一个委托,但它不知道应该指向哪个方法

在方法组仅包含一个方法的特殊情况下,此问题是可解的。在我脑海中,我能想到C#团队不允许它工作的两个原因:

  • 一致性是好的
  • 如果稍后引入另一个重载,将导致完美代码的中断。在调用
    IsInteresting(int)
    的代码中引入编译错误,因为添加了
    IsInteresting(string)
    会给人留下非常糟糕的印象

  • 自C#2.0以来,代表们就一直在场。自C#3.0以来的兰巴斯。Func和Action是.NET framework的功能,从.NET 3.5开始就存在了。Func和Action是下面的委托,只是为了方便(尽管非常方便)。它们下面的功能相同,但可以避免声明委托类型。谓词是返回bool的泛型委托,自.NET2.0以来一直存在


    在编写这篇文章的过程中,有两个答案与代码解决方案,但希望您会发现这很有帮助。

    委托是回调方法的函数签名

    Action和Func都是委托,但都是特定委托类型的缩写

    操作必须有一个参数,并且不能返回值。 Func必须有一个参数,并且必须返回一个值

    考虑以下代表签名:

    delegate void DisplayMessage( string message);
    delegate string FormatTime( DateTime date);
    delegate bool IsAValidAddress( string addressLine1, string addressLine2, int postCode, string country);
    
    第一个签名可以替换为
    操作
    第二个签名可以替换为
    Func

    第三个签名返回一个值,因此只能用
    Func

    唯一的区别是委托可以通过引用传递参数,其中Func<string, string> c1 = (s) => { a(s); returns b(s); };//equivalent to c
    delegate void DisplayMessage( string message);
    delegate string FormatTime( DateTime date);
    delegate bool IsAValidAddress( string addressLine1, string addressLine2, int postCode, string country);