对`Action`委托和lambda表达式的困惑

9
private void StringAction(string aString) // method to be called
{
    return;
}

private void TestDelegateStatement1() // doesn't work
{
    var stringAction = new System.Action(StringAction("a string"));
    // Error: "Method expected"
}

private void TestDelegateStatement2() // doesn't work
{
    var stringAction = new System.Action(param => StringAction("a string"));
    // Error: "System.Argument doesn't take 1 arguments"

    stringAction();
}

private void TestDelegateStatement3() // this is ok
{
    var stringAction = new System.Action(StringActionCaller);

    stringAction();
}

private void StringActionCaller()
{
    StringAction("a string");
}

我不明白为什么TestDelegateStatement3可以工作,但TestDelegateStatement1失败了。在两种情况下,都提供了一个不带参数的方法给Action。它们可以调用一个带一个参数(aString)的方法,但那应该是无关紧要的。它们不需要参数。这是使用lambda表达式不可能完成的,还是我做错了什么?

4个回答

20

就像你所说的,Action不需要任何参数。 如果你这样做:

var stringAction = new System.Action(StringAction("a string"));

实际上你在这里执行了该方法,因此它不是一个方法参数。

如果你这样做:

var stringAction = new System.Action(param => StringAction("a string"));

你需要告诉它你的方法需要一个名为param的参数,而Action类并没有这个参数。

所以正确的做法是:

var stringAction = new System.Action( () => StringAction("a string"));
更紧凑的写法:
Action stringAction = () => StringAction("a string");

空括号用于表示 Lambda 表达式不带任何参数。


@Botz:对于你的“更紧凑”的说法进行了微小的更正:System.Action stringAction = () => StringAction("a string");(编译器没有足够的信息来知道var是一个System.Action)。 - devuxer

5
Action 委托被定义为委托给没有参数并返回 void 的方法。在示例1中,你犯了两个错误:
1. 你试图给出一个带有参数的方法
2. 你调用了该方法,并没有将其作为参数给出(应该是 new Action(methodName)),尽管由于第1点它不会起作用。

在示例2中,你再次犯了同样的错误,你的 lambda 表达式带有一个参数,你应该这样写:
new Action(() => StringAction("a string"));

如果你想创建一个带有参数的委托,你应该这样做:
new Action<string>(myStringParam => StringAction(myStringParam));

所以,在你的情况下,完整的代码如下:


private void StringAction(string aString) // method to be called
{
    return;
}

private void TestDelegateStatement1() // now it works
{
    var stringAction = new Action<string>(StringAction);
    //You can call it now:
    stringAction("my string");
}

private void TestDelegateStatement2() // now it works
{
    var stringAction = () => StringAction("a string");
    //Or the same, with a param:
    var stringActionParam = (param) => StringAction(param);

    //You can now call both:
    stringAction();
    stringActionParam("my string");
}

private void TestDelegateStatement3() // this is ok
{
    var stringAction = new System.Action(StringActionCaller);

    stringAction();
}

private void StringActionCaller()
{
    StringAction("a string");
}

4

我不是专家,但你尝试过这个吗?

public void TestDelegateStatement4
{
    var stringAction = () => StringAction("a string");
}

2
在C# 2.0中,Action委托是一个不接受参数的void委托。 在后续版本中,有通用的Action<T>委托,其中T指定参数类型。
这应该可以工作:
var stringAction = new Action<string>(param => StringAction(param));

甚至更好的是:

var stringAction = new Action<string>(StringAction); // using method group conversion

那么,您可以调用

stringAction("Hello world");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接