【问题标题】:Func<T> as class member, access other members of instanceFunc<T> 作为类成员,访问实例的其他成员
【发布时间】:2014-04-19 18:00:19
【问题描述】:

我有一个问题,是否可以(如果可以,如何)从Func&lt;T, TResult&gt; 代表内部访问class 成员。

例如,我有以下class

class NinjaTurtle
{
    public string Sound { get; set; }
    public Func<string, string> DoNinjaMove { get; set; }
}

现在我想这样做

NinjaTurtle leonardo = new NinjaTurtle();
leonardo.Sound = "swiishhh!";
leonardo.DoNinjaMove = (move) => {
    if(move == "katana slash") return leonardo.Sound;
    return "zirp zirp zirp";
}

问题是,当我定义回调函数时,如何正确访问属性Sound?从函数外部使用对实例的引用是否可以?当我将对象传递给另一个方法时,这是否仍然有效,或者即使这将是 dll 的一部分,并且我会从 dll 中的函数返回对象 leonardo?它会“幸存”序列化/反序列化吗?

(感谢 Vladimir 和 Lee,这个问题现在更具体到我想知道的内容)。

【问题讨论】:

  • 返回 leonardo.Sound; :)
  • 让我重新表述一下 ;-)
  • @Dänu,Vladimir 的评论是一个很好的答案。
  • 看起来DoNinjaMove 应该是Func&lt;string, string&gt;

标签: c# lambda func


【解决方案1】:

您可以使用闭包。闭包将是一个匿名委托或 lambda 表达式,它可以从外部范围引用变量、方法、属性、事件或任何东西(哎呀,这是你的情况!)。

leonardo.DoNinjaMove = (move) => {
    // THIS IS VALID! IT'S A CLOSURE! You can access leonardo reference within
    // the closure!!
    if(move == "katana slash") return leonardo.Sound; 
    return "zirp zirp zirp";
}

不管怎样,DoNinjaMove 就是Func&lt;string, bool&gt;。如果要返回Sound值,应该重构为Func&lt;string, string&gt;

有关闭包如何工作以及为什么可以在其中安全使用外部作用域引用的更多详细信息,请参见 StackOverflow 中的其他问答:

关于在使用附属程序集时是否可以使用闭包等等......

是的,这没有问题。闭包是大多数现代语言都拥有的一个非常有趣的特性,它是包含函数式编程的语言的必备特性。无论如何,这是一个必须具备的功能! :)

【讨论】:

  • 谢谢,已重构。我只是稍微重新提出了这个问题。也许如果你可以重读它,我可以让它像这样工作,但我只是想知道这是不是正确的方法,以及它是否适用于我可以对对象做的所有“事情”(就像我提到的,返回它来自库 [dll],将其传递给另一个方法等。)这是编译器跟踪的内容吗?
  • @Dänu 我刚刚更新了我的答案,其中包含一些指向 StackOverflow 上的其他问答的链接,这将解释为什么它是安全的。当然,您的代码很好! ;)
  • 是的,刚看到。非常感谢,我没想到谷歌关闭;)。是的,今天学到了一些新东西,不,那是假期的目的:)。
  • @Dänu 不客气!如果闭包不存在,匿名委托和 lambda 表达式将毫无意义!
  • 是的,这是有道理的。
【解决方案2】:

如果您从 Google 来到这里,特别想将 Lambda 函数编码为在类主体中声明的类成员,请继续阅读...

我通过 google 找到了这篇文章,因为我正在寻找一种将 Lambda Func 声明为类本身的成员方法的方法。您可以在类中声明 Func,但不能在同一行中直接分配给它。示例:

public class myClass {
    public Func<string,string> DoNinjaMove;  //Can't declare method body here.
}

解决方案是在类的Constructor中分配Lambda函数体,如下所示:

public class myClass {
    public Func<string,string> DoNinjaMove;  //Can't declare method body here.

    public myClass()
    {
        DoNinjaMove = (someString) =>
        {
            //Do something here
            return anotherString;
        }
    }
}

现在 DoNinjaMove 是 myClass 的成员,它的主体也在 myClass 中声明。 DoNinjaMove 可以访问 myClass 的所有成员,并且您可以将 DoNinjaMove 传递给其他类/对象以供他们调用。

除非您完全知道自己在做什么,否则我可能不会推荐这种设计模式。在我的例子中,我使用的另一个库要求我向它传递一个具有特定输入和返回类型的 Lambda 函数,但我需要该函数成为我自己的类的成员,为了优雅和封装。这是我想出的解决方案。

【讨论】:

    【解决方案3】:

    这将在closure 中捕获变量leonardo 并且会起作用,但我认为这不是一个好的设计,但如果没有上下文很难提出不同的建议。

    var leonardo = new NinjaTurtle();
    
    leonardo.Sound = "swiishhh!";
    
    leonardo.DoNinjaMove = (move) =>
    {
       if (move == "katana slash")
       {
          return leonardo.Sound;
       }
       else
       {
          return "zirp zirp zirp";
       }
    }
    

    您可能需要考虑使用 Func&lt;NinjaTurtle, String, String&gt; 并显式传递海龟。

    leonardo.DoNinjaMove = (turtle, move) =>
    {
       if (move == "katana slash")
       {
          return turtle.Sound;
       }
       else
       {
          return "zirp zirp zirp";
       }
    }
    

    但这在我看来仍然不是一个令人信服的设计。

    【讨论】:

    • 是的,我一直在考虑同样的事情。仅使用定义为“外部”的参考感觉是错误的;-)。 Matías 提供的问答链接似乎解决了这个问题。
    • 这取决于您想要实现的目标。如果您使用闭包,对 leonardo 变量的任何更改也会更改方法中的值。如果这是你想要的,那就没有错,
    猜你喜欢
    • 2014-05-18
    • 2012-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多