在C#中引用变量?

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

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

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

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

我意识到这可能是C#“保护”我们自己的方式之一,所以我不是在寻找一个完全相同的,而是一个类似的概念……也就是说,能够引用某些内容其他变量,而不是变量本身的实例。

这是我想到的用例。 我有一个字符串数组。 我想有另一个数组元素的引用数组。 像这样的东西(显然不是有效的代码):

 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" 

当然,ref参数执行此操作,out参数允许您写入特定变量。 但是非参数怎么样? 你可以存储参考吗? 你能保留一系列refs或字典吗?

似乎是否存在使用参数执行此操作的能力,应该有一种方法可以在没有它们的情况下执行此操作。

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

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

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

例如,当你写:

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

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

只读数组引用:

 class ArrayRef { 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[] { new ArrayRef(s, 0), new ArrayRef(s, 1), new ArrayRef(s, 2), new ArrayRef(s, 3), new ArrayRef(s, 4), new ArrayRef(s, 5) }; Console.WriteLine(sr[1]); // == "two" s[1] = "two point zero"; Console.WriteLine(sr[1]); // == "two point zero" 

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

要引用它必须是一个对象,所以你不能引用整数数组中的各个项。 由于字符串是对象,因此只需复制数组中的引用即可引用各个字符串:

 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] }; 

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

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

 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" 

如果有人仍在寻找可能的解决方案。 如果你将每个数组包装到类中,那么可以通过引用数组将其分配给相同包装类的另一个实例。

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

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

 using System; using System.Diagnostics; public class RefArray { TElem[] data; /// Initializes RefArray by creating Ref<T>[]data from values. public RefArray(TElem[] values) { data = values; } /// Creates reference RefArray pointing to same RefArray<T> data. public RefArray(RefArray 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 c = new RefArray(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); RefArray d = new RefArray(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 a = new RefArray(new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" }); RefArray b = new RefArray(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])); } } 

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

 using System; using System.Text; using System.Diagnostics; public class Ref_T { public TValue data; public Ref_T(TValue value) { this.data = value; } } public class RefArray { public readonly Ref_T[] data; /// Initializes RefArray by creating Ref<T>[]data from values. public RefArray(TElem[] values) { data = new Ref_T[values.Length]; for (int i = 0; i < values.Length; i++) { // creates reference members data[i] = new Ref_T(values[i]); } } /// Creates reference RefArray pointing to same RefArray<T> data. public RefArray(RefArray references) { data = references.data; } /// Creates reference RefArray pointing to same Ref<T>[] references. public RefArray(Ref_T[] 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 a) { return a.data; } } public static class RefArrayTest { public static void Usage() { // test ints (struct type) out of order // initialize RefArray e = new RefArray(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); // reference e out of order RefArray f = new RefArray(new Ref_T[] { 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"); } } 

输出:

 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 , ] 

希望这有助于某人。

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

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

变为:

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

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

我们还可以添加一些糖:

 public class Reference { private readonly Func referenceFunc; public Reference(Func referenceFunc) { this.referenceFunc = referenceFunc; } public T Value => this.referenceFunc(); public static implicit operator T(Reference reference) { return reference.Value; } public static implicit operator Reference(Func referenceFunc) { return new Reference(referenceFunc); } public override string ToString() { return this.Value?.ToString(); } } public static class ReferenceExtensions { public static void Add( this ICollection> collection, Func referenceFunc) { collection.Add(referenceFunc); } } 

适用于您的示例:

 string[] s = new string[] { "one", "two", "three", "four", "five", "six" }; IReadOnlyList> sr = new List> { () => 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小提琴

注意:当然,在你有一个支持数组/列表/集合的例子中,仅存储索引而不是Func就足够了。 可能会更快。 此外,一些列表实现(如LinkedList )允许访问列表结构。 (挑剔:然后arrays本身又是参考文献的“列表”,因此s[1]sr[1]应该是等价的,因此sr在这个例子中完全是多余的。)

我怀疑你问的是错误的问题……

考虑以下C#代码:

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

在这种情况下,obj指的是与arr [5]相同的对象,因为arr [5]和obj都是引用。

您提供的示例代码的问题是,当您写入“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"