【问题标题】:Is using Action.Invoke considered best practice?使用 Action.Invoke 是否被认为是最佳实践?
【发布时间】:2012-02-13 11:11:58
【问题描述】:

如果我有以下代码,我应该只调用 Action 还是应该调用 Action.Invoke?

public class ClassA
{
  public event Action<string> OnAdd;

  private void SomethingHappened()
  {
    if (OnAdd != null)
     OnAdd("It Happened"); //Should it be OnAdd.Invoke("It Happened") ???????
  }
}

public class ClassB
{

  public ClassB()
  {
    var myClass = new ClassA();
    myClass.OnAdd += Add;
  }

  private void Add(string Input)
  {
    //do something
  }  
}

【问题讨论】:

  • 在 c# 6 中,使用新的 OnAdd?.Invoke("It Happened"); 语法可能最终会更受欢迎
  • 上面评论的更多细节。该示例使用了 C# 6 的一个特性,即空条件运算符。这 ?如果 OnAdd 为空,则运算符会阻止调用 Invote 方法,从而防止异常。这允许编写放弃以下答案中显示的显式空检查的代码。请参阅msdn.microsoft.com/en-us/magazine/dn802602.aspx 了解更多详情。
  • 这里有一些比较:jacksondunstan.com/articles/3283.

标签: c# .net delegates


【解决方案1】:

两者是等价的,编译器为你把OnAdd("It Happened");转换成OnAdd.Invoke("It Happened");

我想这是一个偏好问题,但我个人更喜欢简洁的形式。

顺便说一句,通常最好在调用类级别委托之前获取类级别委托的本地副本,以避免出现竞争条件,即OnAdd 在检查时不为空,但在它被调用:

private void SomethingHappened()
{
  Action<string> local = OnAdd;
  if (local != null)
  {
    local("It Happened");
  }
}

【讨论】:

  • 我没有看到比赛示例中的差异,抱歉
  • @Jon: 考虑一下是否涉及两个线程,一个线程将OnAdd 设置为空另一个线程测试它是否为空,但在调用它之前...在您的代码中,您最终会得到 NullReferenceException
【解决方案2】:

我在最新的 C# 6 版本中注意到这一点,因为它可能会鼓励更多地使用 Invoke,并认为我会将其添加到这个旧问题中以防它对某人有所帮助:

“旧”方式:

Action<string> doSomething = null; // or not null
if (doSomething != null)
    doSomething("test");

可能的务实方式(类似于空事件委托模式):

Action<string> doSomethingPragmatic = s => { }; // empty - might be overwritten later
doSomethingPragmatic("test");

C# 6:

Action<string> doSomethingCs6 = null; // or not null
doSomethingCs6?.Invoke("test");

// Not valid C#:
// doSomethingCs6?("test")
// doSomethingCs6?.("test")

【讨论】:

  • ReSharper 鼓励这种新的 C# 6 风格。
【解决方案3】:

这两个结构完全等价。

OnAdd("It Happened");

只是语法糖。在幕后,编译器在生成的 MSIL 中发出对 Action&lt;T&gt;.Invoke 的调用。所以使用对你来说更易读的那个(对我来说OnAdd("It Happened"); 已经足够可读了)。

【讨论】:

    【解决方案4】:

    除非你遇到very strange bug around anonymous functions,否则它们是完全等价的。

    我个人通常使用快捷方式,但只是偶尔显式调用Invoke 会更易读。例如,您可能有:

    if (callAsync)
    {
        var result = foo.BeginInvoke(...);
        // ...
    }
    else
    {
        foo.Invoke(...);
        // ...
    }
    

    这里明确使用Invoke 对对称很有用。

    有关委托调用的更多详细信息,请参阅 C# 4 规范的第 15.4 节,尽管它没有在调用 Invoke 方法方面明确指定它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-28
      • 2010-11-08
      • 2011-07-17
      • 1970-01-01
      • 1970-01-01
      • 2011-05-10
      • 1970-01-01
      相关资源
      最近更新 更多