【问题标题】:Can I use a reference inside a C# function like C++?我可以在像 C++ 这样的 C# 函数中使用引用吗?
【发布时间】:2010-07-19 20:26:01
【问题描述】:

在 C++ 中我可以这样做:

 int flag=0,int1=0,int2=1;
 int &iRef = (flag==0?int1:int2);
 iRef +=1;

具有 int1 递增的效果。

我必须修改一些较旧的 c# 代码,如果我能做类似的事情会非常有帮助,但我在想......也许不会。有人吗?

【问题讨论】:

标签: c# reference


【解决方案1】:

更新:下面讨论的功能终于在 C# 7 中添加了。


C# 不支持您想要的功能 - 制作托管局部变量别名。您可以使用 formal parameters 来实现 - 您可以创建作为任何变量别名的形式参数 - 但您不能创建作为任何变量别名的 local

但是,没有技术上的困难阻止我们这样做; CLR 类型系统支持“引用局部变量”。 (它也支持 ref 返回类型,但不支持 ref 字段。)

几年前,我实际上写了一个 C# 的原型版本,它支持 ref locals 和 ref return 类型,它工作得非常好,所以我们有经验证据表明我们可以成功地做到这一点。但是,如果有的话,这个特性不太可能很快被添加到 C# 中。详情请见http://ericlippert.com/2011/06/23/ref-returns-and-ref-locals/

我注意到,如果我是你,我会在任何语言中避免这种情况。编写两个变量共享相同存储空间的程序会导致代码难以阅读、难以理解、难以修改和难以维护。

另见相关问题:Why doesn't C# support the return of references?

【讨论】:

  • 我已经看到这个习惯用法用于命令行工具,它可以从作为参数传递的文件或标准输入中读取:std::istream& is = blah() ? ifs : std::cin。我会用ifsstd::cin 调用一个函数,但其​​他人似乎反对额外的函数调用。
  • @sbi:但是在 C# 中,Stream 已经是引用类型,所以在 C# 中没有问题: Stream s = f ? s1 : s2;,不用担心。这只有在变量是 值类型 时才会变得有趣,如 Gio 的示例所示。在 C# 中,如果不使用形参,就无法为彼此创建两个 值类型 本地别名。
  • 我明白这一点。我只是在回复您的陈述“编写两个变量共享相同存储空间的程序会导致代码难以阅读、难以理解、难以修改和难以维护。”并非总是如此。
【解决方案2】:

可以做到这一点-或者至少与您想要的非常相似-但最好找到另一种方法。例如,您可以将整数包装在一个简单的引用类型中。

如果您仍然想这样做,请参阅 Eric Lippert here 发布的 Ref<T> 课程:

sealed class 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); } }
}

public class Program
{
    public static void Main()
    {
         int flag=0,int1=0,int2=1;

         Ref<int> iRef = (flag == 0 ?
            new Ref<int>(() => int1, z => { int1 = z; }) :
            new Ref<int>(() => int2, z => { int2 = z; }));

         iRef.Value += 1;

        Console.WriteLine(int1);
    }
}

输出:

1

【讨论】:

  • 感谢您的提示。我去看看。
【解决方案3】:

如果您只需要修改函数中的值类型,则使用 ref 关键字传递参数。

int i = 0;

void increment(ref int integer)
{
    integer++;
}

increment(ref i);

【讨论】:

  • 不想为了使用引用而将函数拆分为子函数。无论如何,谢谢。
  • @Gio:我还没有看到一个恰当命名的函数不能提高可读性的案例。如果我必须在解码 Mark 的 Ref&lt;T&gt; 类型(聪明的想法,+1 来自我)和一个接受 ref 参数的函数之间进行选择,我当然更喜欢一个函数。
【解决方案4】:

是的,您可以使用 C# 7.0 做到这一点。它支持返回引用和存储引用。看我的回答here

【讨论】:

    【解决方案5】:

    不怕。您可以使用unsafe 代码和指针来做到这一点,如下所示:

        int flag=0,int1=0,int2=1;
        unsafe
        {
            int *iRef = (flag==0? &int1:&int2);
            *iRef +=1;
        }
    

    (不是说这是个好主意或任何东西:))

    【讨论】:

      【解决方案6】:

      在 C# 中没有直接等效项。有几个选项 -

      您可以使用unsafe code and pointers as suggested by dkackman

      另一种选择是使用保存值的​​引用类型(类)。例如:

      // Using something like
      public class Wrapped<T> {
          public Wrapped(T initial) {
              this.Value = initial;
          }
          public T Value { get; set; }
      }
      
      // You can do:
      bool flag=false;
      var int1 = new Wrapped<int>(0);
      var int2 = new Wrapped<int>(1);
      
      Wrapped<int> iRef = flag ? int2 : int1;
      
      iRef.Value = iRef.Value + 1;
      

      由于您正在使用对类的引用,因此对 iRef 的分配复制了引用,并且上述工作...

      【讨论】:

        【解决方案7】:

        您可以像这样使用Action delegate

        int flag = 0, int1 = 0, int2 = 0;
        Action increment = flag == 0 ? (Action) (() => ++int1) : () => ++int2;
        increment();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-07
          • 1970-01-01
          • 2011-03-11
          • 2013-11-17
          • 2013-02-08
          • 2010-10-05
          相关资源
          最近更新 更多