tags:

views:

96

answers:

2

I am using the following code to pass a property to a lambda expression.

namespace FuncTest
{
    class Test
    {
        public string Name { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test();
            t.Name = "My Test";
            PrintPropValue(t => t.Name);

        }

        private static void PrintPropValue(Func<string> func)
        {
            Console.WriteLine(func.Invoke());
        }

    }
}

This does not compile. I just want the function to be able to take property and be able to evaluate.

+2  A: 
class Program
{
    static void Main(string[] args)
    {
        Test t = new Test();
        t.Name = "My Test";

        //Use a lambda with a free variable
        Func<Test, string> lambda = x => x.Name;
        PrintPropValue(t, lambda);

        //Close the lambda over its free variable.
        //It will refer to the t 
        //captured from the current scope from now on
        //Note: 'lambda' is unchanged, it still can be used 
        //with any 'Test' instance. We just create a (new) 
        //closure using the 'lambda'.
        Func<string> closure = () => lambda(t);
        PrintPropValue(closure);

        //This will still print 'My Test',
        //despite the fact that t in that scope 
        //refers to another variable.
        AnotherT(closure);

        t.Name = "All your " + t.Name + " captured and are belong to us.";
        //This will now print 'All your My Test captured and are belong to us.'
        AnotherT(closure);

    }

    private static void AnotherT(Func<string> closure)
    {
        Test t = new Test();
        t.Name = "My Another Test";

        PrintPropValue(closure);

    }

    private static void PrintPropValue<T>(T instance, Func<T, string> func)
    {
        Console.WriteLine(func(instance));
    }

    private static void PrintPropValue(Func<string> func)
    {
        Console.WriteLine(func());
    }

}
andras
+7  A: 

A Func<string> doesn't have any parameters - but your lambda expression does.

It's not clear whether you really want a Func<Test, string> - in which case you'll need to pass in an instance of Test when you invoke the delegate - or whether you want a Func<string> which captures a particular instance of Test. For the latter:

using System;

class Test
{
    public string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Test t = new Test();
        t.Name = "My Test";
        // Note: parameterless lambda expression
        PrintPropValue(() => t.Name);

        // Demonstration that the reference to t has been captured,
        // not just the name:

        Func<string> lambda = () => t.Name;
        PrintPropValue(lambda);
        t.Name = "Changed";
        PrintPropValue(lambda);
    }

    private static void PrintPropValue(Func<string> func)
    {
        Console.WriteLine(func.Invoke());
    }
}
Jon Skeet