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

本文关键字:实例 对象 引用 返回 新对象 我可以 | 更新日期: 2023-09-27 18:04:08

我正在学习/实验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 unchanged:-("。我在测试中做了一个轻微的变化,我通过了一个非空的Foo实例并修改了属性"a"(vs返回一个新的实例),这工作得很好(也就是说,我可以改变一个现有的对象,就像我在传递对象引用到函数时所期望的那样),我似乎无法从我的委托中获得一个新的实例。

?我只是在代码中做错了什么吗?这到底能不能做到?我想知道为什么这不起作用

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

形式参数ofoo值的复制;突变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 );

如果你想做的只是改变一个变量,那就改变这个变量。如果你只是想执行一个副作用,你不需要从委托中传入或传出任何东西。

我注意到你说你在尝试功能模式。请记住,函数式编程不鼓励变量突变,因此您在这里可能走错了路。

可以返回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 -like委托与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);

但我不建议这样做

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

但是当你给委托的局部变量(即参数)分配新地址时,那么你就失去了传递给委托的原始Foo对象的地址。赋值后,局部变量仅保存新Foo对象的地址。它不影响调用者的foo变量,它仍然保存另一个地址。