引用 C# 中的变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1420186/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 16:20:00  来源:igfitidea点击:

References to variables in C#?

c#

提问by Erik Funkenbusch

In C++, I can do something like this:

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

int i[10] = new int[10];
int *p = &i[5];

Then, I can always know that p points to the 5th element of int array i, regardless of i's contents.

然后,我总是可以知道 p 指向 int 数组 i 的第 5 个元素,而不管 i 的内容如何。

Is there any way to do something similar in C#?

有没有办法在 C# 中做类似的事情?

I realize this is likely one of the ways in which C# "protects" us from ourselves, so i'm not looking for an exact equivelent, but rather a similar concept... that is, being able to refer to the contents of some other variable, rather than the instance of the variable itself.

我意识到这可能是 C#“保护”我们免受我们自己伤害的一种方式,所以我不是在寻找一个确切的等价物,而是一个类似的概念......也就是说,能够参考某些内容其他变量,而不是变量本身的实例。

Here's my use case i'm thinking of. I have an array of strings. I would like to have another array of references to those array elements. Something like this (obviously not valid code):

这是我正在考虑的用例。我有一个字符串数组。我想要对这些数组元素的另一个引用数组。像这样(显然不是有效代码):

string[] s = new string[] { "one", "two", "three", "four", "five", "six" };
stringref[] sr = new stringref[] { &s[0], &s[1], &s[2], &s[3], &s[4], &s[5] };

Console.WriteLine(sr[1]); // == "two"
s[1] = "two point zero";
Console.WriteLine(sr[1]); // == "two point zero"

Certainly, ref paramets do this, and out parameters allow you to write to a specific variable. But what about non-parameters? Can you store a ref? Can you keep an array of refs or a dictionary?

当然, ref 参数可以做到这一点,而 out 参数允许您写入特定变量。但是非参数呢?你能存储一个参考吗?你能保留一组 refs 或字典吗?

It seems like if the ability to do it with parameters is present, there should be a way to do it without them.

似乎如果存在使用参数的能力,那么应该有一种方法可以在没有参数的情况下做到这一点。

采纳答案by Mehrdad Afshari

No. Putting unsafecode aside, which does allow holding pointers to memory locations, there's no way to store a reference to a variable in C#.

不。撇开unsafe代码,它允许保存指向内存位置的指针,没有办法在 C# 中存储对变量的引用。

refand outarguments provide the only means to take a reference but you can't save them anywhere.

refout参数提供了获取引用的唯一方法,但您无法将它们保存在任何地方。

You can workaround this limitation by wrapping fields in a classand using its reference instead. This is what the compiler does to capture variables in closures:

您可以通过将字段包装在 a 中class并使用其引用来解决此限制。这是编译器在闭包中捕获变量所做的工作:

For instance, when you write:

例如,当你写:

int integer = 0;
Action<int> method = i => Console.WriteLine(i + integer);
integer = 42;
method(100); // prints 142, not 100

In the second line, the compiler will have to take out the anonymous method and store it as a separate method in the class. Obviously, that method won't have access to integervariable. It somehow needs to pass a "reference" to integervariable to that anonymous method. Since it's not possible, it'll generate a classwith a field to hold an integer and uses an instance of that class to store the variable. Basically, the local variable is promoted to a field in a class and is stored in the heap.

在第二行中,编译器将不得不取出匿名方法并将其作为单独的方法存储在类中。显然,该方法将无法访问integer变量。它以某种方式需要将integer变量的“引用”传递给该匿名方法。由于这是不可能的,它将生成一个class带有字段的整数,并使用该类的实例来存储变量。基本上,局部变量被提升为类中的一个字段并存储在堆中。

回答by Tal Pressman

I suspect you're asking the wrong question...

我怀疑你问错了问题...

Consider the following C# code:

考虑以下 C# 代码:

MyClass[] arr = new MyClass[] { MyClass(1), MyClass(2) .... };
MyClass obj = arr[5];

In this case, obj isreferring to the same object as arr[5], since both arr[5] and obj are references.

在这种情况下,物镜指相同的对象作为ARR [5],由于两个ARR [5]与obj引用。

The issue with the sample code you provided was that when your write "s[1] = "two point zero"", you aren't actually changing the string in memory - you're making the reference in the array point to a different string. Basically, your C# code is equivalent to the following in C:

您提供的示例代码的问题是,当您写入“s[1] =“两点零””时,您实际上并未更改内存中的字符串 - 您将数组中的引用指向了不同的细绳。基本上,您的 C# 代码等效于 C 中的以下内容:

char **s = malloc( ... );
... set s's members
char *sr = malloc( ... );
sr[1] = s1;
s[1] = "two point zero";
// now, s[1] is "two point zero" while sr[1] is still "one"

回答by dalle

A read-only array reference:

只读数组引用:

class ArrayRef<T>
{
   private T[] array;
   private int index;

   public ArrayRef(T[] array, int index)
   {
      this.array = array;
      this.index = index;
   }

   public static implicit operator T(ArrayRef self)
   {
      return self.array[self.index];
   }
}

var s = new string[] { "one", "two", "three", "four", "five", "six" };
var sr = new ArrayRef<string>[] { new ArrayRef<string>(s, 0), new ArrayRef<string>(s, 1), new ArrayRef<string>(s, 2), new ArrayRef<string>(s, 3), new ArrayRef<string>(s, 4), new ArrayRef<string>(s, 5) };

Console.WriteLine(sr[1]); // == "two"
s[1] = "two point zero";
Console.WriteLine(sr[1]); // == "two point zero"

回答by Guffa

In managed code references are used instead of pointers, as the garbage collector can move objects around in memory at any moment.

在托管代码中使用引用而不是指针,因为垃圾收集器可以随时在内存中移动对象。

To have a reference to something it has to be an object, so you can't have references to the individual items in an integer array. As strings are objects, you can have references to the individual strings by just copying the references in the array:

要引用某个东西,它必须是一个对象,因此您不能引用整数数组中的各个项目。由于字符串是对象,您可以通过复制数组中的引用来引用单个字符串:

string[] s = new string[] { "one", "two", "three", "four", "five", "six" };
string[] sr = new string[] { s[0], s[1], s[2], s[3], s[4], s[5] };

However, as strings are immutable objects you can only use the references to read the items. If you assign a string to a reference in the sr array, you will overwrite the reference instead of changing the object that it points to.

但是,由于字符串是不可变对象,因此您只能使用引用来读取项目。如果您将字符串分配给 sr 数组中的引用,您将覆盖该引用而不是更改它指向的对象。

If you want to change the objects, you will have to have mutable objects. For example:

如果要更改对象,则必须具有可变对象。例如:

StringBuilder[] s = new StringBuilder[] {
   new StringBuilder("one"),
   new StringBuilder("two"),
   new StringBuilder("three"),
};
StringBuilder[] sr = new StringBuilder[] { s[0], s[1], s[2] };

Console.WriteLine(s[1]); // == "two"
sr[1].Append(" point zero");
Console.WriteLine(s[1]); // == "two point zero"

回答by SoLaR

If someone still is looking for possible solution. Playing little with generics it is possible if you wrap each array into class so it can be assigned to another instance of same wrap class by referencing array into it.

如果有人仍在寻找可能的解决方案。如果您将每个数组包装到类中,则可以很少使用泛型,这样就可以通过将数组引用到其中来将其分配给同一包装类的另一个实例。

But this should be used only as proof of concept that is possible to do it. I generally wouldn't recommend use of this, but would suggest redesigning the code in more efficient way. Also its worth to mention you simply can assign array to another array as reference to its elements (DOH).

但这应该仅用作可以做到这一点的概念证明。我通常不建议使用它,但建议以更有效的方式重新设计代码。另外值得一提的是,您只需将数组分配给另一个数组作为对其元素的引用(DOH)。

This being said here is the example code of generic reference array to array data:

这里所说的是通用引用数组到数组数据的示例代码:

using System;
using System.Diagnostics;

public class RefArray<TElem>
{
  TElem[] data;

  /// <summary>Initializes RefArray by creating Ref&lt;T>[]data from values.</summary>
  public RefArray(TElem[] values)
  {
    data = values;
  }

  /// <summary>Creates reference RefArray pointing to same RefArray&lt;T> data.</summary>
  public RefArray(RefArray<TElem> references)
  {
    this.data = references.data;
  }

  public int Length
  {
    get { return data.Length; }
  }

  public TElem this[int index]
  {
    get
    {
      return data[index];
    }
    set
    {
      data[index] = value;
    }
  }
}

public static class RefArrayTest
{

  public static void Usage()
  {

    // test ints (struct type)
    RefArray<int> c = new RefArray<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
    RefArray<int> d = new RefArray<int>(c);
    Debug.Print(string.Format("c[3]: {0,-30}, d[3]: {1}", c[3], d[3]));
    c[3] = 1111;
    Debug.Print(string.Format("c[3]: {0,-30}, d[3]: {1}", c[3], d[3]));
    d[3] = 2222;
    Debug.Print(string.Format("c[3]: {0,-30}, d[3]: {1}", c[3], d[3]));
    d[3] = c[3] + 3333;
    Debug.Print(string.Format("c[3]: {0,-30}, d[3]: {1}", c[3], d[3]));

    // test strings (class type)
    RefArray<string> a = new RefArray<string>(new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" });
    RefArray<string> b = new RefArray<string>(a);
    Debug.Print(string.Format("a[3]: {0,-30}, b[3]: {1}", a[3], b[3]));
    a[3] = "set a";
    Debug.Print(string.Format("a[3]: {0,-30}, b[3]: {1}", a[3], b[3]));
    b[3] = "set b";
    Debug.Print(string.Format("a[3]: {0,-30}, b[3]: {1}", a[3], b[3]));
    a[3] = b[3] + " + take b set a";
    Debug.Print(string.Format("a[3]: {0,-30}, b[3]: {1}", a[3], b[3]));

    // proof of no point since
    string[] n1 = new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
    string[] n2 = n1;
    Debug.Print(string.Format("n1[3]: {0,-30}, n2[3]: {1}", n1[3], n2[3]));
    n1[3] = "set n1";
    Debug.Print(string.Format("n1[3]: {0,-30}, n2[3]: {1}", n1[3], n2[3]));
    n2[3] = "set n2";
    Debug.Print(string.Format("n1[3]: {0,-30}, n2[3]: {1}", n1[3], n2[3]));
    n1[3] = n2[3] + " + take n2 set n1";
    Debug.Print(string.Format("n1[3]: {0,-30}, n2[3]: {1}", n1[3], n2[3]));
  }

}

Also if referenced elements need to be out of order you could add generic Ref_T class to wrap each value as reference:

此外,如果引用的元素需要乱序,您可以添加通用 Ref_T 类来包装每个值作为引用:

using System;
using System.Text;
using System.Diagnostics;

public class Ref_T<TValue>
{  
  public TValue data;
  public Ref_T(TValue value)
  {
    this.data = value;
  }
}

public class RefArray<TElem>
{
  public readonly Ref_T<TElem>[] data;

  /// <summary>Initializes RefArray by creating Ref&lt;T>[]data from values.</summary>
  public RefArray(TElem[] values)
  {
    data = new Ref_T<TElem>[values.Length];
    for (int i = 0; i < values.Length; i++)
    {
      // creates reference members
      data[i] = new Ref_T<TElem>(values[i]); 
    }
  }

  /// <summary>Creates reference RefArray pointing to same RefArray&lt;T> data.</summary>
  public RefArray(RefArray<TElem> references)
  {
    data = references.data;
  }

  /// <summary>Creates reference RefArray pointing to same Ref&lt;T>[] references.</summary>
  public RefArray(Ref_T<TElem>[] references)
  {
    data = references;
  }

  public int Length
  {
    get { return data.Length; }
  }

  public TElem this[int index]
  {
    get { return data[index].data; }
    set { data[index].data = value; }
  }

  public override string ToString()
  {
    StringBuilder sb = new StringBuilder();
    int count = data.Length;
    for (int i = 0; i < count; i++ )
      sb.Append(string.Format("[{0}]:{1,-4}, ", i, data[i].data));
    return sb.ToString();
  }

  public static implicit operator Array(RefArray<TElem> a)
  {
    return a.data;
  }
}

public static class RefArrayTest
{

  public static void Usage()
  {    
    // test ints (struct type) out of order
    // initialize 
    RefArray<int> e = new RefArray<int>(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
    // reference e out of order
    RefArray<int> f = new RefArray<int>(new Ref_T<int>[] 
      { e.data[8], e.data[6], e.data[4], e.data[2], e.data[0], 
        e.data[9], e.data[7], e.data[5], e.data[3], e.data[1] 
      });

    Debug.WriteLine("Initial: ");
    Debug.WriteLine("e: [" + e + "]");
    Debug.WriteLine("f: [" + f + "]\r\n");

    e[3] = 1111;
    Debug.WriteLine("e[3] = 1111;");
    Debug.WriteLine("e: [" + e + "]");
    Debug.WriteLine("f: [" + f + "]\r\n");

    f[3] = 2222;
    Debug.WriteLine("f[3] = 2222;");
    Debug.WriteLine("e: [" + e + "]");
    Debug.WriteLine("f: [" + f + "]\r\n");

    f[3] = e[3] + 3333;
    Debug.WriteLine("f[3] = e[3] + 3333;");
    Debug.WriteLine("e: [" + e + "]");
    Debug.WriteLine("f: [" + f + "]\r\n");

    Array.Reverse(f);
    Debug.WriteLine("Array.Reverse(f);");
    Debug.WriteLine("e: [" + e + "]");
    Debug.WriteLine("f: [" + f + "]\r\n");
  }

}

outputs:

输出:

Initial: 
e: [[0]:0   , [1]:1   , [2]:2   , [3]:3   , [4]:4   , [5]:5   , [6]:6   , [7]:7   , [8]:8   , [9]:9   , ]
f: [[0]:8   , [1]:6   , [2]:4   , [3]:2   , [4]:0   , [5]:9   , [6]:7   , [7]:5   , [8]:3   , [9]:1   , ]

e[3] = 1111;
e: [[0]:0   , [1]:1   , [2]:2   , [3]:1111, [4]:4   , [5]:5   , [6]:6   , [7]:7   , [8]:8   , [9]:9   , ]
f: [[0]:8   , [1]:6   , [2]:4   , [3]:2   , [4]:0   , [5]:9   , [6]:7   , [7]:5   , [8]:1111, [9]:1   , ]

f[3] = 2222;
e: [[0]:0   , [1]:1   , [2]:2222, [3]:1111, [4]:4   , [5]:5   , [6]:6   , [7]:7   , [8]:8   , [9]:9   , ]
f: [[0]:8   , [1]:6   , [2]:4   , [3]:2222, [4]:0   , [5]:9   , [6]:7   , [7]:5   , [8]:1111, [9]:1   , ]

f[3] = e[3] + 3333;
e: [[0]:0   , [1]:1   , [2]:4444, [3]:1111, [4]:4   , [5]:5   , [6]:6   , [7]:7   , [8]:8   , [9]:9   , ]
f: [[0]:8   , [1]:6   , [2]:4   , [3]:4444, [4]:0   , [5]:9   , [6]:7   , [7]:5   , [8]:1111, [9]:1   , ]

Array.Reverse(f);
e: [[0]:0   , [1]:1   , [2]:4444, [3]:1111, [4]:4   , [5]:5   , [6]:6   , [7]:7   , [8]:8   , [9]:9   , ]
f: [[0]:1   , [1]:1111, [2]:5   , [3]:7   , [4]:9   , [5]:0   , [6]:4444, [7]:4   , [8]:6   , [9]:8   , ]

Hope this helps someone.

希望这可以帮助某人。

回答by BatteryBackupUnit

It's possible to achieve this using delegates. Nowadays it doesn't even require a lot of code:

可以使用委托来实现这一点。现在它甚至不需要很多代码:

int i[10] = new int[10];
int *p = &i[5];

becomes:

变成:

int[] i = new int[10];
Func<int> p = () => i[5];

performing p()always returns the 6th element (index 5, 0 based).

执行p()总是返回第 6 个元素(索引 5,基于 0)。

We can also add some sugar:

我们还可以加点糖:

public class Reference<T>
{
  private readonly Func<T> referenceFunc;

  public Reference(Func<T> referenceFunc)
  {
    this.referenceFunc = referenceFunc;
  }

  public T Value => this.referenceFunc();

  public static implicit operator T(Reference<T> reference)
  {
    return reference.Value;
  }

  public static implicit operator Reference<T>(Func<T> referenceFunc)
  {
    return new Reference<T>(referenceFunc);
  }

  public override string ToString()
  {
    return this.Value?.ToString();
  }
}

public static class ReferenceExtensions
{
  public static void Add<T>(
      this ICollection<Reference<T>> collection,
      Func<T> referenceFunc)
  {
    collection.Add(referenceFunc);
  }
}

Applied to your example:

应用于您的示例:

string[] s = new string[] { "one", "two", "three", "four", "five", "six" };
IReadOnlyList<Reference<string>> sr = new List<Reference<string>>
{
  () => s[0],   
  () => s[1],
  () => s[2],
  () => s[3],
  () => s[4],
  () => s[5],
};

Console.WriteLine(sr[1]); // == "two"
s[1] = "two point zero";
Console.WriteLine(sr[1]); // == "two point zero"

.Net Fiddle

.Net小提琴

Note: Of course, in the example where you have a backing array/list/collection it'd be enough just to store the index instead of a Func<T>. Might be faster. Also some list implementations, like LinkedList<T>, allow access to the list structure. (Nitpicking: Then again the array itself is a "list" of references and thus s[1]and sr[1]should be equivalent, srthus completely superfluous in the example.)

注意:当然,在您有一个支持数组/列表/集合的示例中,仅存储索引而不是Func<T>. 可能会更快。还有一些列表实现,比如LinkedList<T>,允许访问列表结构。(挑剔:然后数组本身又是一个引用的“列表”,因此s[1]并且sr[1]应该是等效的,sr因此在示例中完全多余。)