我正在学习/试验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”(vs返回一个新实例)并且工作正常(也就是说,我可以像我一样改变现有对象期望在将对象引用传递给函数时)我似乎无法从我的委托中获取新实例。
所以?我只是在代码中做错了吗?这可以完成吗?很想明白为什么这不起作用。
答案 0 :(得分:5)
正式参数o
是值foo
的副本;变异o
不会改变foo
。这跟你说的一样:
int x = 1;
int y = x;
y = 2;
这不会改变x
。 y
是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 );
如果您想要做的就是改变变量,那么改变变量。如果您只想执行副作用,则无需向代表传递任何内容。
我注意到你说你正在尝试功能模式。请记住,函数式编程不鼓励变量变异,所以你可能会在这里走错路。
答案 1 :(得分:4)
您可以返回Foo
作为lambda表达式的返回值:
Func<Foo> someFunc = o =>
{
return new Foo { A = "A new instance of o?" };
};
如果您确实需要返回bool
,则可以返回Tuple<bool, Foo>
:
Func<Tuple<bool, Foo>> someFunc = o =>
{
return Tuple.Create(true, new Foo { A = "A new instance of o?" });
};
或者,如果你真的确实确定你想要那个,你可以声明自己的自定义Func
- 就像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);
但我不建议这样做。
答案 2 :(得分:2)
您正在将Foo
对象的引用(即地址)传递给您的代理人。该地址被分配给委托的参数o
(将其视为局部变量)。当您在委托中更改Foo
对象时,您将引用对象并更改该地址上的内容。这就是对象变化的原因。
但是当您为委托的局部变量(即参数)分配新地址时,您只是丢失了传递给委托的原始Foo
对象的地址。赋值后,局部变量只保存新Foo
对象的地址。它不会影响调用者的foo
变量,它仍然包含另一个地址。