【问题标题】:What is the value being passed into this lambda expression?传递给这个 lambda 表达式的值是什么?
【发布时间】:2018-03-02 10:29:13
【问题描述】:

如果这是一个愚蠢的问题,请原谅我,但我不知道该去哪里找。我有这段代码编译得很好:

static int Main(string[] args)
{
    var parserResult = CommandLine.Parser.Default.ParseArguments<Options>(args);

    parserResult.WithParsed<Options>(options => OnSuccessfulParse(options));
    parserResult.WithNotParsed<Options>(errs =>
    {
        var helpText = HelpText.AutoBuild(parserResult, h =>
        {
            return HelpText.DefaultParsingErrorsHandler(parserResult, h);
        }, e =>
        {
            return e;
        });
        Console.WriteLine(helpText);
        ReturnErrorCode = ErrorCode.CommandLineArguments;
    });

    return (int)ReturnErrorCode;
}

我的查询与这行代码有关:

parserResult.WithParsed<Options>(options => OnSuccessfulParse(options));

我明白,Lambda Expressionleft 值是 =&gt;valueright 的值是表达式

options 到底是什么?为什么会编译?它工作得很好。但它是什么?

我不知道这是否有帮助:

根据我对这一点的理解,我可能会找错树。我承认我在这个概念上苦苦挣扎。任何解释表示赞赏。

我看到几个问题,但在我的情况下无法解释。

更新

OnSuccessfulParse 声明是:

private static void OnSuccessfulParse(Options options)

WithParsed 代码提供here

【问题讨论】:

  • 好吧,大概WithParsed&lt;T&gt; 接受Action&lt;T&gt; 或者Func&lt;T, T&gt;(我们不知道OnSuccessfulParse 是什么,这没有帮助)。但是,得到的值是库的一部分——您查看过该库的代码和文档吗?这不是一般的 C# 语言问题 - 它是特定于库的。
  • optionsOptions 类的实例,据我所知,它代表成功解析的命令行选项。因此,如果库成功将命令行选项解析为给定类 (Options),它将调用回调。该回调是您传递给WithParsed 的内容。所以你的回调将被调用,然后OnSuccessfulParse 方法将被调用(通过你的代码)。
  • 我的理解是options是i/p参数,以lambda表达式表示。
  • 您使用的底层框架/库创建/管理 Options 对象,并在某些时候将其传递给您的 lambda。
  • 我从来没有提到检查options类型,我说检查@的 987654344@(根据传递的是什么[...]

标签: c# lambda


【解决方案1】:

Lambda 表达式是Action&lt;Options&gt; 类型的委托。它是从解析器回调到您的代码中,通知您解析已成功,并将解析结果获得的Options 对象传递给您。

options 而言,它只是您为要传递给OnSuccessfulParse 方法的参数选择的名称。这里完全没有必要 - 这个方法组等效调用将编译和运行相同:

parserResult.WithParsed<Options>(OnSuccessfulParse);

这里是github projectWithParsed&lt;T&gt;方法的定义:

public static ParserResult<T> WithParsed<T>(this ParserResult<T> result, Action<T> action)
{
    var parsed = result as Parsed<T>;
    if (parsed != null) {
        action(parsed.Value);
    }
    return result;
}

此方法相当简单:它获取解析结果,尝试将其转换为成功的解析,如果转换有效,则调用您提供的委托。 WithNotParsed&lt;T&gt; 方法转换为不成功的NotParsed&lt;T&gt; 结果,如果转换有效则进行调用。

【讨论】:

  • 对!所以,optionsparsed.Value 对象。而parsed 的类型为Parsed&lt;T&gt;。因为action 是以OnSuccessfulParse 为参数的函数调用。
  • 我会接受这个作为答案,因为它专门针对我的场景。
【解决方案2】:

这是表示功能块的 C# (.Net) 方式。 本质上,Action&lt;Type&gt; 是一种可调用类型,大致意思是 pass an instance of Type in and execute the block

例如我们可以写

public void Do(){
  this.CallStuff(s => Console.WriteLine(s)); // or you can use a method group and do this.CallStuff(Console.WriteLine);
}

public void CallStuff(Action<string> action){
  var @string = "fancy!";
  action(@string);
}

在这种情况下,s 类型是 string

在您的示例中,在某处定义了一个名为 Options 的类型,并将其传递给一个动作。

此外,如果您查看反编译代码,传递给操作的匿名方法块在您的类中编译为静态匿名类型(因为 c# 不支持动态代码块,例如 obj-c 支持)。

要研究的另一件事是 Func&lt;out type&gt; -> 它们与 Action&lt;&gt; 基本相同,只是泛型定义中的 LAST 类型是它们返回的类型。

UPD @elgonzo 提出了一个很好的观点——Action&lt;&gt;Func&lt;&gt; 实际上只是代表;这意味着您可以定义一个真正的方法并将其作为 Action 或 Func 传递,然后不会编译匿名静态类。

但是在实践中你会看到很多代码都定义了这些内联代码,然后内联代码块需要驻留在某个方法中的某个地方,因此编译器会将其放入 statis 匿名类中。

【讨论】:

  • 澄清一下:您可以在这里看到“s”或“options”作为变量名。不管你叫它“options”、“myVar”还是只是(最不可读的)“a”。
  • 您对 Action 的解释具有误导性。准确地说, Action<...> 和 Func<...> 是委托,而不是静态匿名类型。碰巧在您的 Action 委托的示例实例中引用了一个匿名方法。由于它们是委托,Action<...> 和 Func <...> 委托也可以引用“普通”命名的实例/静态方法。
【解决方案3】:

究竟什么是选项?

options 是由 lambda 表达式表示的委托的 i/p 参数

为什么会编译?

仅仅是因为它遵守编译器的所有语法和语义规则;)就像(x)=&gt;x+1 是表示Func&lt;int,int&gt; 的lambda 表达式(也可以有另一个委托来匹配相同的签名)

如果您的方法期望 Func&lt;int,int&gt; 作为参数并且传递的参数是 (x)=&gt;x+1,那么编译器会将此推断为 int 类型的 x。编译的 IL 代码相当于将委托实例作为参数传递给方法。喜欢:

call((x)=&gt;x+1) 将被编译为call(new Func&lt;int,int&gt;(myMethod)) 其中调用方法定义为:

void Call(Func<int,int> someparam) {} 

但它是什么?

我认为上面的回复应该已经解决了这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多