3

我正在学习/尝试 C# 中的一些功能模式,但遇到了一个我无法解释的问题。我确定这是一个简单的答案(我希望如此),但我很难看到它。可能与闭包等有关,而我无法开箱即用是对我隐瞒了答案!

这是我的实验:我正在尝试从函数委托中返回特定类的全新实例。

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?'

当然,我只是在我的输出中得到“foo 未更改 :-(”。我在测试中做了一点改动,我传入了一个非空 Foo 实例并修改了属性“A”(而不是返回一个新实例)和效果很好(也就是说,我可以像将对象引用传递给函数时所期望的那样改变现有对象)我似乎无法从我的委托中获取新实例。

所以?我只是在代码中做错了吗?这完全可以做到吗?很想知道为什么这不起作用。

4

3 回答 3

5

形参o是值的副本foo;mutatingo不会发生变异foo。就像你说的一样:

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

那没有改变xy是的值的副本,而x不是 的别名x

你多虑了这个问题。如果你想要一个改变本地的委托,那么只需编写一个改变本地的委托:

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

如果您只想改变一个变量,那么就改变这个变量。如果您只想执行副作用,则无需从委托中传入或传出任何内容。

我注意到您说您正在试验功能模式。请记住,函数式编程不鼓励变量突变,因此您可能会走上错误的道路。

于 2013-03-16T14:46:20.310 回答
4

您可以返回Foolambda 表达式的返回值:

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

或者,Tuple<bool, Foo>如果您确实需要返回 a ,则可以返回 a bool

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

或者,如果你真的确定你想要这样,你可以用一个参数Func声明你自己的自定义委托:out

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

但我不建议这样做。

于 2013-03-16T14:23:09.293 回答
2

您正在将对象的引用(即地址)传递Foo给您的委托。该地址被分配给o委托的参数(将其视为局部变量)。当您更改Foo委托中的对象时,您将要引用对象并更改该地址上的内容。这就是对象发生变化的原因。

但是,当您将新地址分配给委托的局部变量(即参数)时,您只会丢失Foo传递给委托的原始对象的地址。赋值后局部变量只保存新Foo对象的地址。它不影响foo调用者的变量,它仍然拥有另一个地址。

于 2013-03-16T14:24:58.353 回答