为什么C#不支持引用的返回?

时间:2011-06-14 05:51:50

标签: c# .net reference return-type

我已经读过.NET支持返回引用,但C#没有。有特殊原因吗?为什么我不能做这样的事情:

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

4 个答案:

答案 0 :(得分:187)

这个问题是my blog on June 23rd 2011的主题。谢谢你提出的好问题!

C#团队正在考虑使用C#7。有关详细信息,请参阅https://github.com/dotnet/roslyn/issues/5233

更新:该功能已进入C#7!


你是对的; .NET确实支持方法,这些方法返回对变量的托管引用。 .NET还支持包含对其他变量的托管引用的局部变量。 (但请注意,.NET不支持包含对其他变量的托管引用的字段数组,因为这会使垃圾收集故事过于复杂。另外,“对变量的托管引用”类型不可转换为对象,因此不能用作泛型类型或方法的类型参数。)

评论者“RPM1984”出于某种原因要求引用这一事实。 RPM1984我建议您阅读CLI规范分区I第8.2.1.1节“管理指针和相关类型”,以获取有关.NET此功能的信息。

完全可以创建一个支持这两个功能的C#版本。然后你可以做像

这样的事情
static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

然后用

调用它
int a = 123;
int b = 456; 
ref int c = ref Max(ref a, ref b); 
c += 100;
Console.WriteLine(b); // 556!

我从经验上知道可以构建一个支持这些功能的C#版本因为我已经这样做了。高级程序员,特别是移植非托管​​C ++代码的人,经常会要求我们提供更多的C ++ - 比如使用引用来做事情的能力,而不必在实际使用指针和固定内存的大麻烦。通过使用托管引用,您可以获得这些好处,而无需支付搞乱垃圾回收性能的成本。

我们已经考虑过这个功能,并且实际上已经实施了足够的功能,以便向其他内部团队展示以获得他们的反馈。然而,此时根据我们的研究,我们认为该功能没有广泛的吸引力或引人注目的使用案例,使其成为真正支持的语言功能。我们还有其他更高的优先级和有限的时间和精力,因此我们不会很快就会执行此功能。

此外,正确执行此操作需要对CLR进行一些更改。现在,CLR将ref-returns方法视为 legal 无法验证,因为我们没有检测器来检测这种情况:

ref int M1(ref int x)
{
    return ref x;
}

ref int M2()
{
    int y = 123;
    return ref M1(ref y); // Trouble!
}

int M3()
{
    ref int z = ref M2();
    return z;
}

M3返回M2局部变量的内容,但该变量的生命周期已经结束!可以编写一个检测器,用于确定违反堆栈安全性的ref-returns的使用。我们要做的就是编写这样的探测器,如果探测器无法证明堆栈的安全性,那么我们就不允许在程序的那一部分使用ref返回。这样做不是一项大量的开发工作,但测试团队确保我们真正掌握了所有案例是一个很大的负担。另一件事就是将功能的成本提高到现在效益不会超过成本的程度。

如果你能为我描述为什么你想要这个功能,我真的很感激。我们从真实客户那里获得的关于他们为什么需要它的信息越多,它就越有可能在某一天进入产品。这是一个可爱的小功能,如果有足够的兴趣,我希望能够以某种方式将它送到客户手中。

(另请参阅相关问题Is it Possible to Return a Reference to a Variable in C#?Can I use a reference inside a C# function like C++?

答案 1 :(得分:22)

您正在讨论返回对值类型的引用的方法。我知道C#中唯一的内置示例是值类型的数组访问器:

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

现在创建一个该结构的数组:

var points = new Point[10];
points[0].X = 1;
points[0].Y = 2;

在这种情况下points[0],数组索引器,正在返回对struct的引用。编写自己的索引器(例如自定义集合)是不可能的,它具有相同的“返回引用”行为。

我没有设计C#语言,所以我不知道支持它的所有背后的原因,但我认为简短的回答可能是:没有它我们可以相处得很好。

答案 2 :(得分:1)

您可以随时执行以下操作:

public delegate void MyByRefConsumer<T>(ref T val);

public void DoSomethingWithValueType(MyByRefConsumer<int> c)
{
        int x = 2;
        c(ref x);
        //Handle potentially changed x...
}

答案 3 :(得分:0)

C#7.0 支持返回引用。请参阅我的回答here