3

I'm learning/experimenting with some functional patterns within C# and I've hit a bump I can't quite explain. I'm sure it's a simple answer (I hope) but I'm struggling to see it. Likely has to do with closures, etc and my inability to get out-of-box is hiding the answer from me!

Here's my experiment: I'm trying to return a brand new instance of a particular class from within a function delegate..

public class Foo{
    string A { get; set ; }
}

static void Main( string[] args ){
    // the delegate...
    Func<Foo,bool> someFunc = o => {
        o = new Foo { A = "A new instance of o?" };
        return true;
    };

    Foo foo = null;   // was hoping to replace this via delegate
    var myFunc = someFunc;
    var result = myFunc( foo );

    if ( foo == null )
        Console.WriteLine( "foo unchanged :-(" );
    else
        Console.WriteLine( foo.A ); // hoping for 'A new instance of o?'

Of course, I just get "foo unchanged :-(" in my output. I made a slight variation on the test where I passed in a non-null Foo instance and modified the property "A" (vs returning a new instance) and that worked okay (that is, I can mutate an existing object just like I would expect when passing object references to functions) I just can't seem to get a new instance out of my delegate.

So? Am I just doing something wrong in the code? Can this be done at all? Would love to understand why this doesn't work.

robcom88
  • 63
  • 1
  • 6

3 Answers3

5

Formal parameter o is a copy of the value of foo; mutating o does not mutate foo. It's the same as when you say:

int x = 1;
int y = x;
y = 2;

That doesn't change x. y is a copy of the value of x, not an alias to x.

You're overthinking the problem. If you want to have a delegate that mutates a local then just write a delegate that mutates a local:

Foo foo = null;   // was hoping to replace this via delegate
Action mutateFoo = () => { foo = new Foo() { A = "whatever"}; };
mutateFoo();
if ( foo == null )
    Console.WriteLine( "foo unchanged :-(" );
else
    Console.WriteLine( foo.A );

If all you want to do is mutate a variable then mutate the variable. There's no need to pass anything in or out from the delegate if you just want to perform a side effect.

I notice that you said that you were experimenting with functional patterns. Remember, functional programming discourages variable mutation, so you might be going down a false path here.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • In your example (using the ints), I understand that holds for _value types_ but if `x` and `y` are _reference types_ then, after `y = x` statement, aren't I now dealing with two references to the same object istance? Any change I make via `y` affects `x`. Did I miss something there? – robcom88 Mar 16 '13 at 15:10
  • Oh, and I knew someone would get me on that functional comment ;-) I understand that mutation is _not_ the answer as I progress down this functional road. Good catch and thanks for the reminder. – robcom88 Mar 16 '13 at 15:11
  • @robcom88: if `x` and `y` refer to the same object with field `z` then `x.z` and `y.z` refer to the same field, but `x` and `y` do not refer to *each other*. They both refer to the *same object*. – Eric Lippert Mar 16 '13 at 15:13
  • 1
    @robcom88: Think about it like this. You have two drawers with labels `x` and `y`. In `x` there is a piece of paper that says `123 Sesame Street`. When you say `y = x;` that means get out the photocopier and put a second piece of paper that says `123 Sesame Street` into `y`. `x` and `y` now refer to the same house, but if you then throw away the piece of paper in `x` and replace it with one that says `1600 Pennsylvania Avenue`, that doesn't change the contents of drawer `y`. The key takeaway here is **references are just another kind of value**. – Eric Lippert Mar 16 '13 at 15:15
4

You can return the Foo as the return value of the lambda expression:

Func<Foo> someFunc = o =>
{
    return new Foo { A = "A new instance of o?" };
};

Or you can return a Tuple<bool, Foo> if you really need to return a bool:

Func<Tuple<bool, Foo>> someFunc = o =>
{
    return Tuple.Create(true, new Foo { A = "A new instance of o?" });
};

Or, if you're really really sure you want that, you can declare your own custom Func-like delegate with an out parameter:

delegate TResult FuncOut<T, TResult>(out T arg);
FuncOut<Foo, bool> someFunc = (out Foo o) =>
{
     o = new Foo { A = "A new instance of o?" };
     return true;
};

Foo foo;
var result = someFunc(out foo);

But I wouldn't recommend that.

dtb
  • 213,145
  • 36
  • 401
  • 431
  • I agree! No 'out' parms ;-) So, return the object within a Tuple is the way I'll probably go. I think that lazyberezovsky has given me a clue as to the 'why' but I'm marking this as answer because you've given me the 'what' and the workaround I need. Thanks – robcom88 Mar 16 '13 at 14:52
2

You are passing reference (i.e. address) of Foo object to your delegate. That address is assigned to parameter o of delegate (consider it as local variable). When you are changing Foo object in delegate, then you are going to referenced object and changing what's sitting on that address. That's why object changes.

But when you are assigning new address to local variable of delegate (i.e. parameter), then you just losing address of original Foo object which was passed into delegate. After assignment local variable just holds address of new Foo object. It does not affect foo variable of caller, which still holds another address.

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • 1
    Okay, that makes perfect sense. Ultimately this answers my underlying question as to _why_ my test wasn't working (wish that I could give partial answer credit). I think I has missed a fundamental lesson in object references here that was being obscured by the delegate aspect. In truth, my test wouldn't work even with a traditional (i.e., non-delegate) method. Appreciate the help! – robcom88 Mar 16 '13 at 14:57