6

I have a question wheater or not it is possible (and if it is, how) to access class members from inside a Func<T, TResult> delegate.

For example, I have the following class:

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

Now I'd like to do this

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

The problem is, how do I correctly access the property Sound, when I define the callback function? Is it OK to just use the reference to the instance from outside the function? Would this still work when I pass the object to another method, or even when this would be part of a dll, and I would return the object leonardo from a function in the dll? Would it "survive" serialization / deserialization?

(Thanks Vladimir and Lee, the question is now more specific to what I would like to know).

Dänu
  • 5,791
  • 9
  • 43
  • 56

3 Answers3

6

You can use closures. A closure will be an anonymous delegate or lambda expression which may reference variables, methods, properties, events or anything from an outer scope (oops, it's your case!).

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";
}

Anyway, DoNinjaMove is Func<string, bool>. If you want to return Sound value, it should be refactored to Func<string, string>.

Further details about how closures work and why you can safely use outer scope's references within them can be found on this other Q&A here in StackOverflow:

About if using closures would work when working with satellite assemblies and so...

Yes, there's no problem with that. Closures are a very interesting feature that most modern languages own and it's a must-have feature for languages that have incorporated functional programming. Anyway, it's a must-have feature! :)

Community
  • 1
  • 1
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • Thanks, refactored it. I just refrased the question a little bit. Maybe if you could reread it, I got it working like this, but I just wondered wheather this is the right way to do it and if it would apply to all "things" that I could do with the object (like i mentioned, return it from a library [dll], pass it to another method, etc.) Is this something the compiler keeps track of? – Dänu Apr 19 '14 at 18:14
  • @Dänu I've just updated my answer with some link to some other Q&A on StackOverflow which will explain you why it's safe. Of course, your code is fine!! ;) – Matías Fidemraizer Apr 19 '14 at 18:15
  • Yes, just saw it. Thanks a lot, I didn't think to google for closures ;). Yay, learned something new today, no that's what holidays are for :). – Dänu Apr 19 '14 at 18:18
  • @Dänu You're welcome! Anonymous delegates and lambda expressions would make no sense if closures wouldn't exist! – Matías Fidemraizer Apr 19 '14 at 18:24
4

If you came here from Google specifically wanting to code a Lambda function as a class member declared inside the class body, read on...

I found this post through google, because I was looking for a way to declare the Lambda Func as a member method of the class itself. You can declare a Func inside of a class but you can't directly assign to it in the same line. Example:

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

The solution is to assign the Lambda function body inside the Constructor of the class like this:

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

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

Now DoNinjaMove is a member of myClass and it's body is also declared inside myClass. DoNinjaMove has access to all members of myClass, and you get the ability to pass DoNinjaMove to other classes/objects for them to call it.

I probably wouldn't recommend this design pattern unless you absolutely know what you're doing. In my case, another library I was using demanded I pass it a Lambda function with a specific input and return type, but I needed the function to be a member of my own class where it had access to class data for the sake of elegance and encapsulation. This is the solution I came up with.

JamesHoux
  • 2,999
  • 3
  • 32
  • 50
2

This will capture the variable leonardo in a closure and will work but I don't think this is a good design but it is hard to suggest something different without context.

var leonardo = new NinjaTurtle();

leonardo.Sound = "swiishhh!";

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

You may want to consider using Func<NinjaTurtle, String, String> and pass the turtle in explicitly.

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

But this does still not look like a convincing design to me.

Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
  • Yeah I've been thinking about the same thing. Just using the reference defined "outside" feels wrong ;-). The Q&A link provided by Matías seems to address this. – Dänu Apr 19 '14 at 18:16
  • That depends on what you want to achieve. If you use a closure any change to the `leonardo` variable will also change the value within the method. If this is what you want, then there is nothing wrong with that, – Daniel Brückner Apr 19 '14 at 18:18