C# 引用类型数组

13

我该如何做类似这样的事情?

int v1 = 4;
int v2 = 3;
int v3 = 2;
int v4 = 1;

int [] vars = new int [] {ref v1, ref v2, ref v3, ref v4};

for (var i = 0; i < 4; i++) {
    ChangeVar (vars [i], i);
}

void ChangeVar (ref int thatVar, int newValue) {
    thatVar = newValue;
}

编辑:

我想这样做是因为其他类直接访问这些变量。例如,v1可能是某物体的宽度,v2可能是某物体的高度。我的一些类使用宽度变量来限制其从用户获取的输入长度。有些类使用高度变量来执行其他操作。但是我希望能够使用循环来编辑这些变量,因为现在编辑过程是这样的:

int indexOfVarToChange = GetIndex ();

switch (indexOfVarToChange) {
    case 0:
        int newValue = GetNewValue ();
        width = newValue;
        break;
    case 1:
        int newValue = GetNewValue ();
        height = newValue;
        break;
}
我必须手动重新分配变量,因为我不能在循环中使用对这些变量的引用数组。我有超过30个唯一的变量需要这样做,这真是太麻烦了。 我想备选方案是将所有这些变量移动到字典中,并拥有所有键的数组,并将每个键传递给编辑函数。

你为什么想这样做?难道不能传递数组和索引吗? - Konrad Rudolph
2
@TreeTree:这被称为 int*... - user541686
@Mehrdad:创造性思维。但是,正如您所知,指针生命周期存在许多注意事项,我怀疑OP并不了解。 - sehe
除非你想陷入“不安全”的地狱,否则你不能这样做。说实话,我不明白为什么你要这样做,因为你可以使用数组来保存v1到v4,而不需要任何奇怪的操作。 - harold
3个回答

6

不可以。

您仍然可以直接分配来修改元素,但只能在原地编辑:

 vars[2] += 42;

但是我刚刚测试过这个可以工作:

using System;
public class Test
{
        private static void assign(ref int i)
        {
             i = 42;
        }

        public static void Main()
        {
              var vars = new [] { 1,2,3,4 };
              Console.WriteLine(vars[2]);
              assign(ref vars[2]);
              Console.WriteLine(vars[2]);
        }
}

请查看 在线演示 http://ideone.com/fz36y

输出结果

3
42

更新:包装器

作为一种智力锻炼,我想出了这种扭曲的机制,可以仍然实现您想要的功能(但成本比简单地对所有int进行强制类型转换还要高):

private class Wrap<T> where T : struct
{
    public T Value;

    public static implicit operator Wrap<T>(T v) { return new Wrap<T> { Value = v }; }
    public static implicit operator T(Wrap<T> w) { return w.Value; }

    public override string ToString() { return Value.ToString(); }
    public override int GetHashCode() { return Value.GetHashCode(); }
    // TODO other delegating operators/overloads
}

现在,Wrap<int> 会表现得与普通 int 类型类似(需要在比较、相等性和运算符领域中进行更多的工作)。您可以使用它来编写代码,并使其按照您想要的方式工作:
private static void assign(ref int i)
{
    i = 42;
}

public static void Main()
{
    Wrap<int> element = 7;
    var vars = new Wrap<int>[] {1, 2, element, 3, 4};
    Console.WriteLine(vars[2]);
    assign(ref vars[2].Value);
    Console.WriteLine(element);

    Console.ReadKey();
}

输出:

7
42

查看它的演示效果:http://ideone.com/b0m7T


3

假设出于某种原因您确实需要像这样做,我认为在不使用不安全代码的情况下,最接近的方法是通过更改您的代码来添加一个间接层,即创建一个名为Holder的小类,它“持有”int(或任何T)。

namespace ConsoleApplication33 {
  public static class Program {
    private static void Main() {
      var t1=new Holder<int>(4);
      var t2=new Holder<int>(3);
      var t3=new Holder<int>(2);
      var t4=new Holder<int>(1);

      var vars=new[] {t1, t2, t3, t4};

      for(var i=0; i<4; i++) {
        ChangeVar(vars[i], i);
      }
    }

    static void ChangeVar<T>(Holder<T> thatVar, T newValue) {
      thatVar.Value=newValue;
    }

    public class Holder<T> {
      public T Value { get; set; }

      public Holder(T value=default(T)) {
        Value=value;
      }
    }
  }
}

我们有相同的想法,但我的版本包含隐式转换和 GetHashCode 和 ToString 的转发,作为事情变得透明的开始 :) - sehe
2
我建议Value应该是一个字段而不是属性。虽然大多数类的字段代表不应该暴露给客户端的实现细节,但我认为Holder<T>的整个目的是持有和公开其值,就像数组公开其元素一样。公开字段并不会有意义地限制类的设计,但将其包装在属性中会使像MyPointHolder.Value.X += 3;Threading.Interlocked.Increment(ref CounterHolder.Value);这样的有用操作变得不可能。 - supercat

0
这个类的 InitializeAll 方法使用Linq表达式和反射来工作。它与您想要的代码具有相同的意图。它将v1、v2、v3和v4分别初始化为0、1、2和3。
using System;
using System.Linq.Expressions;

namespace ArrayOfReferences
{
    public class InitializeMultipleVariables
    {
        int v1;
        int v2;
        int v3;
        int v4;

        public void InitializeAll()
        {
            Initialize(
                () => v1, 
                () => v2, 
                () => v3, 
                () => v4);
        }

        public void Initialize(params Expression<Func<int>>[] intExpressions)
        {
            for (int i = 0; i < intExpressions.Length; i++)
            {
                var expr = intExpressions[i].Body as System.Linq.Expressions.MemberExpression;                
                var fieldInfo = this.GetType().GetField(expr.Member.Name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
                fieldInfo.SetValue(this, i);
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接