【问题标题】:Why doesn't C# support the return of references?为什么 C# 不支持返回引用?
【发布时间】:2017-06-29 05:20:34
【问题描述】:

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

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

【问题讨论】:

  • 嗯...引用好吗?
  • C# 有值和引用类型,两者都可以返回你的意思。
  • 我说的是return ref x
  • 4 年后,您可以通过 C# 7 做到这一点 :)

标签: c# .net reference return-type


【解决方案1】:

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

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

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


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

评论者“RPM1984”出于某种原因要求引用这一事实。 RPM1984 我鼓励您阅读 CLI 规范 Partition 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-returning 方法视为合法不可验证,因为我们没有检测这种情况的检测器:

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 返回。这样做的开发工作量并不大,但要确保我们确实掌握了所有案例,这对测试团队来说是一个很大的负担。这只是将功能成本提高到目前收益不超过成本的另一件事。

如果您能向我描述您为什么需要此功能,我将不胜感激。我们从真实客户那里获得的关于他们为什么想要它的信息越多,它就越有可能在某天成为产品。这是一个可爱的小功能,如果有足够的兴趣,我希望能够以某种方式将其提供给客户。

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

【讨论】:

  • @EricLippert:我没有令人信服的例子,这只是我想知道的。出色而令人信服的反应
  • @Eric:在你的例子中,从M2返回后保持y活着不是更合适吗?我希望这个功能能够像捕获本地人的 lambdas 一样工作。还是您提出的行为是因为它是 CLR 处理该场景的方式?
  • @Eric:恕我直言,让属性返回对值类型的引用的能力是 .net 语言中的一个主要遗漏。如果 Arr 是一个值类型的数组(例如 Point),可以说例如Arr(3).X=9 并且知道 Arr(9).X 甚至 SomeOtherArray(2).X 的值都没有改变;如果 Arr 是某个引用类型的数组,则不存在此类保证。数组的索引运算符返回引用这一事实非常有用;我认为非常不幸的是,没有其他类型的集合可以提供这样的功能。
  • Eric,提供有关场景的反馈(正如您在上一段中建议的那样)以最大限度地提高被看到的机会的最佳方式是什么?微软连接? UserVoice(具有任意 10 个帖子/投票限制)?还有什么?
  • @ThunderGr:这就是“不安全”的 C# 哲学——如果您编写的代码可能是内存不安全的,那么 C# 坚持将其标记为“不安全”,以便您承担责任为了内存安全。 C# 已经具有该功能的不安全版本,前提是所讨论的变量是非托管类型。问题是 C# 团队是否应该制作一个不安全的版本来处理托管类型。如果这样做使开发人员可以轻松编写可怕的错误,那么它就不会发生。 C# 不是 C++,它是一种可以轻松编写可怕错误的语言。 C# 在设计上是安全的。
【解决方案2】:

您正在谈论返回对值类型的引用的方法。我所知道的 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] 数组 indexer 正在返回对结构的引用。编写具有相同“返回引用”行为的自己的索引器(例如用于自定义集合)是不可能的。

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

【讨论】:

  • Tom 在询问返回对变量的引用的方法。变量不必是值类型,当然这通常是人们想要返回引用的方法时想要的。否则,很好的分析;您是正确的,在 C# 语言中,复杂表达式生成 ref 到用户可以操作的变量的唯一位置是数组索引器。 (当然还有接收者和字段之间的成员访问运算符“.”,但这很明显是对变量的访问。)
【解决方案3】:

你总是可以这样做:

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

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

【讨论】:

    【解决方案4】:

    C# 7.0 支持返回引用。看我的回答here

    【讨论】:

      猜你喜欢
      • 2011-07-06
      • 1970-01-01
      • 1970-01-01
      • 2019-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-04
      相关资源
      最近更新 更多