【问题标题】:anonymous delegates in C#C# 中的匿名委托
【发布时间】:2010-11-01 23:02:09
【问题描述】:

我不会是唯一一个厌倦了为需要委托的事物定义和命名委托的人。例如,我想以可能来自其他线程的表单调用 .Refresh(),所以我编写了以下代码:

private void RefreshForm()
{
    if (InvokeRequired)
        Invoke(new InvokeDelegate(Refresh));
    else
        Refresh();
}

我什至不确定我是否必须这样做,我只是读了足够多的书,害怕它在以后的某个阶段不起作用。
InvokeDelegate 实际上是在另一个文件中声明的,但我是否真的需要一个专门用于此的整个委托?根本没有任何通用代表吗?
我的意思是,例如,有一个 Pen 类,但也有 Pens。pen-of-choice 所以你不必重新制作整个东西。不一样,但我希望你明白我的意思。

【问题讨论】:

  • C# 中没有“匿名代表”之类的东西。有委托(就像你正在使用的那样)和匿名方法(只能通过委托访问)。
  • 我猜由于 C# 规范使用术语“匿名方法”,这将是正确的方法。然而,这个术语被很多人用来表示使用匿名方法作为参数实例化的委托。因此,它们的含义相同,但是某人可能使用一个术语而不是另一个术语的上下文通常取决于是否发生委托分配,或者是否将匿名方法直接传递给另一个方法。我欢迎任何相反的论点。

标签: c# delegates anonymous-methods


【解决方案1】:

是的。在 .NET 3.5 中,您可以使用 FuncAction 代表。 Func 委托返回一个值,而 Action 委托返回 void。以下是类型名称的样子:

System.Func<TReturn> // (no arg, with return value)
System.Func<T, TReturn> // (1 arg, with return value)
System.Func<T1, T2, TReturn> // (2 arg, with return value)
System.Func<T1, T2, T3, TReturn> // (3 arg, with return value)
System.Func<T1, T2, T3, T4, TReturn> // (4 arg, with return value)

System.Action // (no arg, no return value)
System.Action<T> // (1 arg, no return value)
System.Action<T1, T2> // (2 arg, no return value)
System.Action<T1, T2, T3> // (3 arg, no return value)
System.Action<T1, T2, T3, T4> // (4 arg, no return value)

我不知道为什么他们每个都停在 4 个 args 上,但这对我来说一直都足够了。

【讨论】:

  • 4 不是一个具体的规则,但过去你至少应该考虑以某种方式组合参数。
  • 当我将一些较旧的 .NET 代码移植到 3.5 并看到一个委托时,我将它们换成 Func 或 Action。
  • 下一个版本的框架将包含四个以上参数的Func和Action。
【解决方案2】:

您可以使用 Action 委托,如下所示:

private void RefreshForm()
{
    if (InvokeRequired) Invoke(new Action(Refresh));
    else Refresh();
}

或者,使用 lambda 语法:

private void RefreshForm()
{
    if (InvokeRequired) Invoke((Action)(() => Refresh()));
    else Refresh();
}

终于有了匿名委托语法:

private void RefreshForm()
{
    if (InvokeRequired) Invoke((Action)(delegate { Refresh(); }));
    else Refresh();
}

【讨论】:

  • 奇怪的是,第二种和第三种语法不起作用,因为它不是委托类型,因此无法将其转换为 System.Delegate。但是,Action 绝对是我想要的。
  • 匿名委托不能以这种方式传递给 Invoke,因为它们不是委托类型。你在这里写的甚至不能编译。
  • 嘿,这就是我在发帖前没有检查的结果。我会尽快解决的。
  • 将 lambda 和匿名委托转换为 Action 可以解决问题。很抱歉造成混乱。
  • 这篇文章很有帮助。
【解决方案3】:

在这种特定情况下,您可以(并且应该)只使用MethodInvoker 来执行此操作...这就是它存在的原因。

if (InvokeRequired)
    Invoke(new MethodInvoker(Refresh));
else
    Refresh();

如果您正在做其他事情,您可以使用 Func 或 Action 如果它们适合您的用例。

【讨论】:

  • 这是 .NET 2.0 的最佳解决方案
【解决方案4】:

短版:

Invoke((MethodInvoker)delegate { Refresh(); });

那么你也可以去掉InvokeRequired的勾选;你可以直接调用它。如果您需要传递参数,也可以使用,因此不需要其他特定于参数的委托(也可以与无参数的 Action 委托一起使用):

private void SetControlText(Control ctl, string text)
{
    Invoke((MethodInvoker)delegate { ctl.Text = text; });
}

【讨论】:

  • 为什么要创建一个匿名委托只是为了将其转换为现有的委托类型?您应该直接使用委托类型。
  • @Brian:你的意思是这样的:Invoke(new MethodInvoker(delegate { ctl.Text = text; })); ?据我所知,它产生的 IL 代码与我上面的代码完全相同,所以我猜这取决于个人喜好。
  • MethodInvoker和Action有什么区别?另外,我正在检查 InvokeRequired,因为它似乎是一种更好的做法,例如更快。
  • @Fredrik:Brian 的意思是使用 new MethodInvokder(Refresh) 而不是 (MethodInvoker)delegate { Refresh(); }
  • 使用 Invoke((MethodInvoker)Refresh) 或 Invoke(new MethodInvoker(Refresh)) 而不是 Invoke((MethodInvoker)delegate { Refresh(); }) ... 后者创建了一个附加级别通过创建一个只调用 Refresh() 的匿名方法来间接实现,而不是为 Refresh() 本身创建一个委托。
【解决方案5】:

我真的需要一个完整的代表吗 专为此?不在那里 任何通用代表?

定义您自己的委托确实可以使调试更容易,因为 Intellisense 可以告诉您参数的名称。例如,您编写这样的委托:

public delegate int UpdateDelegate(int userID, string city, string, state, string zip);

当您使用它的代码时,.NET 会通知您参数名称、委托名称等,因此如果您不确定如何使用某些东西,委托定义中有很多上下文。

但是,如果您不介意牺牲 Intellisense,系统命名空间中已经定义了一类委托,可用作临时委托:

Func<T>
Func<T, U>
Func<T, U, V>
Func<T, U, V, W>
Action, Action<T>
Action<T, U>
Action<T, U, V>
Action<T, U, V, W>

.NET 2.0 中仅存在 ActionAction,但它很容易声明一个助手类,其中包含此类杂项即席函数所需的剩余委托。

【讨论】:

    【解决方案6】:

    是的,有通用委托。 Action&lt;T1, T2...&gt; 是一个泛型委托,它接受一些参数并且不返回值,Func&lt;T1, T2...R&gt; 是一个泛型委托,它接受一些参数并返回一个值。

    【讨论】: