Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

lambda

func

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

like image 269
Dänu Avatar asked Apr 19 '14 18:04

Dänu


3 Answers

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:

  • How do closures work behind the scenes? (C#)

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! :)

like image 92
Matías Fidemraizer Avatar answered Oct 04 '22 20:10

Matías Fidemraizer


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.

like image 27
JamesHoux Avatar answered Oct 04 '22 20:10

JamesHoux


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.

like image 43
Daniel Brückner Avatar answered Oct 04 '22 22:10

Daniel Brückner