【问题标题】:Is it Possible to Return a Reference to a Variable in C#? [duplicate]是否可以在 C# 中返回对变量的引用? [复制]
【发布时间】:2010-12-27 23:17:05
【问题描述】:

例如,我可以返回对双精度值的引用吗?

这就是我想做的:

ref double GetElement()
{
   ......
   // Calculate x,y,z
   return ref doubleArray[x,y,z];
}

像这样使用它

void func()
{
   GetElement()=5.0;
}

这就像在 C++ 中返回一个双指针...... 我知道我写它的方式是错误的..但是有没有正确的方法呢?

【问题讨论】:

  • 你想将元素分配给数组还是获取它的值?你想要一个读东西或写东西的函数吗?
  • 这不是重点。在 C++(不是 C++/CLI)中,没有值/引用类型的概念。您可以完美地引用双精度(当然不是 .NET 意义上的引用):int x = 42; int& y = x;。无论如何,恐怕这对 OP 不是很有帮助;)
  • @John Saunders:不,它不是的意思是“试图返回对值类型的实例的引用”。这意味着尝试返回对值类型变量的引用,这在 CLR 类型系统中是完全合理和合法的。一个变量有一个位置、一个类型和一个生命周期;只要已知生命周期不短于引用的生命周期,对这样的变量进行引用是合法的。 (在 CLR 类型系统中,而不是在 C# 中。)记住 数组元素是变量,而不是值。
  • 如果您对此感兴趣:我认为(尽管我必须检查)公共语言运行时(使用托管引用)允许这样做,但 C# 不支持它.
  • 我是唯一一个认为 OP 提供的示例是代码异味的人吗?分配给获得某些东西的方法对我来说似乎很奇怪。或者就此而言,将某些东西分配给一个方法......

标签: c# reference return-value


【解决方案1】:

更新:C# 7 现在支持所需的功能。


CLR 类型系统确实支持 ref-returning 方法,并且我已经编写了 C# 编译器的实验原型,它支持您想要的功能。 (原型也实现了 ref 类型的局部变量,但是 ref 类型的字段在 CLR 类型系统中是非法的。)

你已经准确地找到了我为原型选择的语法,这意味着要么伟大的思想相似,要么傻瓜永远不会不同。

虽然原型工作得非常好,但这不太可能使该栏成为 C# 语言的下一个版本的功能。很少有客户想要这个功能,实现起来相当昂贵,我们有一个列表,只要你的手臂更重要的功能,还有其他方法可以让这种事情工作而不会给类型系统增加这种复杂性。这些都是对执行该功能的巨大“反对”。

例如,您可以创建一对委托:

struct Ref<T>
{
    private readonly Func<T> getter;
    private readonly Action<T> setter;
    public Ref(Func<T> getter, Action<T> setter)
    {
        this.getter = getter;
        this.setter = setter;
    }
    public T Value { get { return getter(); } set { setter(value); } }
}

var arr = new int[10];
var myref = new Ref<int>(()=>arr[1], x=>arr[1]=x);
myref.Value = 10;
Console.WriteLine(myref.Value);

这比使用 ref 返回实现的相同功能要慢得多,但好处是您可以在 ref 不合法的地方创建 Ref&lt;T&gt;。例如,您可以将Ref&lt;T&gt; 存储在一个字段中,而使用 ref-returning 方法则无法做到这一点。

如果您有一个非常棒的引人入胜的场景来说明为什么您需要 ref-returning 方法,我会喜欢 听到它。我们拥有的真实场景越多,这种功能就越有可能在该语言的假设未来版本中实现。

另请参阅相关问题:

Can I use a reference inside a C# function like C++?

Why doesn't C# support the return of references?

还有我关于这个主题的博文:

http://ericlippert.com/2011/06/23/ref-returns-and-ref-locals/

【讨论】:

  • 我有一个场景,我怀疑它是否称得上令人敬畏或引人注目,但我确实再次遇到了:一个将结构公开为属性的类。如果仅支持Class.Struct.Property = value,则可以有效地更新该值。因为它是你必须使用Class.Struct = new Struct { Property = value } 或添加冗余助手属性到Class。即使Ref&lt;Struct&gt; 也不能解决这个问题。
  • 我在概念上同意,但看起来像引用类型的可变结构对于效率至关重要。如果分配/释放一百万个引用实例与一百万个结构一样快,那将不是问题!
  • @EricLippert:假设一个有一个可变类PointClass,其中Int32 字段或属性称为XY,一个有一个IList&lt;PointClass&gt; myList。有没有半途而废的方法来允许myList[2].X = 5; 工作而不允许外部代码在不经过myList 的情况下对myList[2].X 进行未来修改?给定一个IList&lt;Point&gt; myList2,像{var tmp=myList2[2]; tmp.X = 5; myList[2] = tmp;} 这样的代码比我想象的任何类都更清晰、更安全。既然Point 在语义上是一对整数,为什么不使用行为类似整数的数据类型呢?
  • @kmote:再次阅读答案。我在 2017 年对其进行了更新。请注意所有大写字母中的“更新”位。所以是的,它应该被更新,而且确实如此。
  • @kmote:现在,如果您想花精力更新网站,我的建议是删除问题,而不是更新答案。这个问题是(1)重复的,(2)基于现在错误的假设。将其删除需要一些工作,因为其他问题将其标记为重复。
【解决方案2】:

更新

此功能已添加到 C# 7。您可以使用您在问题中发布的语法。例如:

double[,,] doubleArray = new double[10,10,10];

ref double GetElement()
{
   var (x,y,z) = (1,2,3);
   return ref doubleArray[x, y, z];
}

Eric Lippert's answer 详细介绍。我可能会删除这个答案,但由于它是公认的答案,我无法删除它。

原答案

C# 中的值类型始终按值传递。对象总是通过值传递它们的引用。正如 Axarydax 指出的那样,这会改变“不安全”的代码。

避免这种约束的最简单、最安全的方法是确保你的替身以某种方式附加到一个对象上。

public class MyObjectWithADouble {
    public double Element {get; set;} // property is optional, but preferred.
}

...
var obj = new MyObjectWithADouble();
obj.Element = 5.0

我还想指出,我对您预期如何将双精度数分配给三维数组感到有些困惑。你可能想澄清你的目的。

我想我对你现在要做什么有了更好的理解。您希望返回给定数组中值的位置,然后能够更改该位置中的值。这种模式打破了 C# 的一些预期范式,所以我建议考虑其他方法来实现您正在寻找的东西。但如果这样做真的有意义,我会做更多这样的事情:

public class 3dArrayLocation {public int X; public int Y; public int Z;}

...

public 3dArrayLocation GetElementLocation(...)
{
    // calculate x, y, and z
    return new 3dArrayLocation {X = x, Y = y, Z = z}
}

...

var location = GetElementLocation(...);
doubleArray[location.X, location.Y, location.Z] = 5.0;

【讨论】:

  • 别担心,数组不是 3 维而是 8 维的......一些带有 Q-Learning 的机器学习需要 :)
  • @Betamoo:听起来很有趣。查看我的编辑。
【解决方案3】:

不,这在 C# 中是不可能的。只能通过引用传递参数。

但是,您可以使用属性实现相同的结果:

double Element
{
    get { return doubleArray[x,y,z]; }
    set { doubleArray[x,y,z] = value; }
}

void func()
{
   Element = 5.0;
}

【讨论】:

    【解决方案4】:

    可以unsafe code 中执行此操作,并拥有一个返回指向双精度指针的方法:

    unsafe double* GetElementP(){
    ...
    }
    

    【讨论】:

    • 虽然这是可能的,但我不会推荐它......可能有更好的方法来解决这个问题,但 OP 需要提供更多关于他想要做什么的细节
    • 您必须将数组固定到位,以便垃圾收集器不会移动它。
    • @Eric:我知道这对你来说可能有点离题,但如果有人在博客上写下 C++/CLI 的 interior_ptrpin_ptr 如何映射到 CLR 类型系统,那就太好了,C# 的指针变量如何比较,以及 C++/CLI 指针是否有任何 C# 等效项。
    • @Ben:Stan Lippman 或 Herb Sutter 更适合询问 C++/CLI 细节;我不熟悉他们。 C++ 固定指针的 C# 等效项是使用“固定”语句声明的指针类型的局部变量。
    【解决方案5】:

    经过一番思考,为什么不使用这样的函数发送要设置的值:

    void SetElement(double value)
    {
       ......
       // Calculate x,y,z
       doubleArray[x,y,z]=value;
    }
    

    并使用它:

    void func()
    {
       SetElement(5.0);
    }
    

    但是,我仍然会选择您的一个有用的答案...

    【讨论】:

      猜你喜欢
      • 2021-11-08
      • 2021-06-09
      • 2014-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-21
      • 2011-11-23
      • 2021-12-29
      相关资源
      最近更新 更多