Func<T> as class member, access other members of instance
Asked Answered
F

3

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

Francophile answered 19/4, 2014 at 18:0 Comment(4)
return leonardo.Sound; :)Beaconsfield
Let me rephrase that ;-)Goulette
@Dänu, Vladimir's comment is a good answer.Hyderabad
It looks like DoNinjaMove should be a Func<string, string>.Jonijonie
C
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! :)

Conduplicate answered 19/4, 2014 at 18:6 Comment(4)
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?Goulette
@Francophile 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!! ;)Scarificator
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 :).Goulette
@Francophile You're welcome! Anonymous delegates and lambda expressions would make no sense if closures wouldn't exist!Scarificator
C
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.

Cocainize answered 18/7, 2018 at 0:14 Comment(0)
M
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.

Metempirics answered 19/4, 2014 at 18:6 Comment(2)
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.Goulette
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,Sidra

© 2022 - 2024 — McMap. All rights reserved.