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.

有帮助吗?

解决方案 2

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.

其他提示

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.

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top