我可以从C#委托中返回对新对象实例的引用吗?

问题描述:

我正在学习/试用C#中的一些功能模式,并且我碰到了一个我不能解释的凹凸。我相信这是一个简单的答案(我希望),但我很难看到它。可能与封闭等有关,我无法摆脱箱子隐藏我的答案!我可以从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实例中传递并修改了属性“A”(vs返回一个新实例)并且工作正常(也就是说,我可以改变现有对象就像我期望将对象引用传递给函数时一样)我似乎无法从我的代理中获取新实例。

那么,我只是在代码中做错了什么?这可以完成吗?想知道为什么这不起作用。

您可以返回Foo作为lambda表达式的返回值:

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

或者你可以返回Tuple<bool, Foo>如果你真的需要返回一个bool

​​

或者,如果你真的真的确定你想要的,你可以声明你自己的自定义Func -like代表与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); 

但我不会建议。

+0

我同意!没有'out'parms ;-)所以,返回Tuple中的对象是我可能会去的方式。我认为lazyberezovsky给了我一个关于'为什么'的线索,但我将其标记为答案,因为你已经给了我'需要的'和''的解决方法。谢谢 – robcom88 2013-03-16 14:52:34

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

但是,当您将新地址分配给代理的局部变量(即参数)时,那么您仅丢失传递到委托的原始Foo对象的地址。赋值局部变量之后只保存新的Foo对象的地址。它不影响调用者的foo变量,该变量仍包含另一个地址。

+1

好的,这很有道理。最终,这回答了我的潜在问题,为什么我的测试不起作用(希望我能给出部分答案功劳)。我认为我错过了这里的对象引用中的一个基本教训,这个引导被代表方面所模糊。事实上,即使使用传统(即非委托)方法,我的测试也不会起作用。感谢帮助! – robcom88 2013-03-16 14:57:37

形式参数o拷贝的值foo;突变o不会变异foo。这和你说的一样:

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

这并不改变xyx,而不是一个别名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); 

如果你想要做的是变异的变量,然后变异的变量。如果您只是想执行副作用,则无需从委托中传入或传出任何内容。

我注意到你说你正在试验功能模式。请记住,功能性编程阻止变异突变,所以你可能会在这里走错路。

+0

在你的例子中(使用整数),我明白这适用于_value types_,但如果'x'和'y'是_reference types_,那么在'y = x'语句之后,我现在不是处理两个引用同样的目标?我通过'y'所做的任何更改都会影响'x'。我错过了那里的东西吗? – robcom88 2013-03-16 15:10:37

+0

哦,我知道有人会让我看到这个功能性评论;-)我明白,当我沿着这条功能性的道路前进时,突变不是答案。良好的接触和感谢提醒。 – robcom88 2013-03-16 15:11:58

+0

@ robcom88:如果'x'和'y'指向字段为'z'的同一个对象,那么'xz'和'yz'指向同一个字段,但'x'和'y'不指向*其他*。它们都指向*相同的对象*。 – 2013-03-16 15:13:00