Is using Action.Invoke considered best practice?

2020-02-24 08:14发布

问题:

If I have the below code, should I just call the Action or should it call 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
  }  
}

回答1:

The two are equivalent, the compiler converts OnAdd("It Happened"); into OnAdd.Invoke("It Happened"); for you.

I guess it's a matter of preference, however I personally prefer the terser form.

As an aside, it is generally preferable to take a local copy of a class level delegate before invoking it to avoid a race condition whereby OnAdd is not null at the time that it is checked, but is at the time that it is invoked:

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


回答2:

The two constructs are perfectly equivalent.

OnAdd("It Happened");

is just syntactic sugar. Behind the scenes the compiler emits a call to Action<T>.Invoke in the resulting MSIL. So use the one that's more readable to you (for me OnAdd("It Happened"); is readable enough).



回答3:

Something I noticed on this with the latest C# 6 release as it may encourage Invoke to be used more and thought I'd add it to this old question in case it helps someone:

"Old" way:

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

Possible pragmatic way (similar to empty event delegate pattern):

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")


回答4:

They're exactly equivalent unless you run into a very strange bug around anonymous functions.

Personally I typically use the shortcut form, but just occasionally it ends up being more readable to explicitly call Invoke. For example, you might have:

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

Here the explicit use of Invoke is useful for symmetry.

See section 15.4 of the C# 4 spec for more details of delegate invocation, although it doesn't explicitly specify it in terms of calling the Invoke method.