【问题标题】:Action to Delegate : new Action or casting Action?Action to Delegate : 新动作还是铸造动作?
【发布时间】:2013-05-02 00:33:27
【问题描述】:

我发现了两种不同的方法来使用 Action 初始化委托:

创建一个新动作或强制转换为动作。

Delegate foo = new Action(() => DoNothing(param));
Delegate bar = (Action)(() => DoNothing(param));

这两种语法有区别吗?

哪个更好,为什么?

在此示例中使用了委托,因为语法对于使用 lambda 表达式调用 BeginInvoke 或 Invoke 等方法很有用,并且将 lambda 表达式转换为操作很重要

static main 
{
    Invoke((Action)(() => DoNothing())); // OK
    Invoke(new Action(() => DoNothing())); // OK
    Invoke(() => DoNothing()); // Doesn't compil
}

private static void Invoke(Delegate del) { }

但有趣的是编译器授权了这个:

Action action = () => DoNothing();
Invoke(action);

【问题讨论】:

  • 致您的编辑:您的staticInvoke 方法中会有哪些有意义的内容?
  • 没什么有趣的。这个问题的目的只是为了理解这两种语法之间的区别。当我调用 Dispatcher.Invoke() (msdn.microsoft.com/en-us/library/cc647509(v=vs.100).aspx) 时,我真正的实现是在 WPF 应用程序中
  • 我明白了。你应该还记得Action Delegate,因为Action 派生自Delegate。因此,即使您使用接收Delegate 的重载,您也可以给它一个Action。示例:Action bar = () => DoNothing(param); someDispatcher.BeginInvoke(bar);(参见Dispatcher.BeginInvoke)。另外:在较新版本的框架 .NET 4.5 中,Dispatcher.InvokeAction 的重载,但这只是为了方便起见,请参阅 msdn.microsoft.com/en-us/library/hh199416.aspx
  • 我明白这一点。但是编译器不会将 lambda 表达式隐式转换为委托,而是将其转换为操作(它是委托)。这是另一个主题:)

标签: c# casting delegates lambda action


【解决方案1】:

这两条指令没有区别。在这两个指令中,创建了一个新的 Action 实例。

下面的 IL 代码似乎证实了这一点。

控制台程序:

class Program
{
    static void Main(string[] args)
    {
        Delegate barInit = (Action)(() => DoNothing());
        Delegate fooInit = new Action(() => DoNothing());
    }

    private static void DoNothing() { }
}

IL 代码:

// First instruction
IL_0000: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2'
IL_0005: brtrue.s IL_0018

IL_0007: ldnull
IL_0008: ldftn void CodeMachineTest.Program::'<Main>b__0'()

// Create a new Action instance for the instruction (Action)(() => DoNothing())
IL_000e: newobj instance void [mscorlib]System.Action::.ctor(object, native int)

IL_0013: stsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2'

IL_0018: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2'
IL_001d: pop

// Second instruction
IL_001e: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
IL_0023: brtrue.s IL_0036

IL_0025: ldnull
IL_0026: ldftn void CodeMachineTest.Program::'<Main>b__1'()
IL_002c: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
IL_0031: stsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3'

IL_0036: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
IL_003b: pop
IL_003c: ret

【讨论】:

    【解决方案2】:

    在我看来没有区别。

    new Action(() => DoNothing(param));
    

    这只是创建了一个新的 Action 并传递了一个 Lambda 表达式,编译器将处理它并确保一切都正常连接。

    (Action)(() => DoNothing(param));
    

    这是可行的,因为像这样的 lambda 方法不返回值也不接受参数,因此编译器可以根据它通过委托系统来验证它是否“可映射”到 Action。

    它们或多或少是相同的,取决于任何类型的编译器优化,很难说哪个性能更高,也许你应该测试一下性能并自己看看?

    这是一个有趣的问题,是对委托系统以及 Linq 和表达式如何适应的探索。

    new Func<string>(() => "Boo!");
    

    或多或少等同于:

    (Func<String>)() => "Boo!";
    

    据我所知,他们最终都会通过委托系统(认为Invoke() 等)失败,如果您确实测试了性能并分享了您的结果,那将会很有趣。

    【讨论】:

    • 我做了一些测试,没有任何区别。反汇编后,IL和机器码是一样的。
    • 这并不是特别有趣,它们只是导致相同 IL 的两种语法。所以这和表演没有关系,所以你想让他分享什么?如果您想了解详细信息,请阅读 C# 语言规范。第一个表达式在7.6.10.5 委托创建表达式,第二个表达式在6.5 匿名函数转换。章节编号来自文档的 5.0 版。请注意,7.6.10.5 明确表示其处理方式与 6.5 相同。
    【解决方案3】:

    没有区别,它们只是相同的两种语法。就个人而言,我使用最后一个,因为它更短。

    但是为什么需要Delegate 类型的变量呢?在大多数情况下,您希望变量与实例具有相同的类型,然后您可以使用

    var bar = (Action)(() => DoNothing(param));
    

    Action bar = () => DoNothing(param);
    

    而不是

    Delegate bar = (Action)(() => DoNothing(param));  // (from your question)
    

    【讨论】:

    • Invoke 或 BeginInvoke 等一些方法需要一个 Delegate,并且必须强制转换或创建一个 Action 才能使用 lambda 表达式。
    • @Hyralex 但是所有具体的委托类型,包括System.Action,都从System.Delegate派生(通过System.MulticastDelegate)。因此方法InvokeBeginInvoke 和其他成员被继承。所以你可以说例如Action bar = () =&gt; DoNothing(param); bar.BeginInvoke( ... );。所以我不明白。
    • 我说的是像 Dispatcher 这样的现有对象的 BeginInvoke 和 Invoke。我编辑了我的问题以提供更多示例。
    • (这条评论取代了之前的评论。)我对 InvokeBeginInvoke 的看法是错误的。它们在System.Delegate 上不存在,因为在那个“级别”上不知道签名。所以它们不是继承的,它们是在具体类型System.Action(和任何其他具体委托类型)上声明的。另一方面,方法DynamicInvoke 是继承方法的一个示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-09
    相关资源
    最近更新 更多