2009-09-14 14 views
11

In C++ kann ich so etwas tun:Verweise auf Variablen in C#?

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

Dann kann ich immer wissen, dass p Punkte auf das 5. Element int array i, unabhängig von Inhalt des i.

Gibt es eine Möglichkeit, etwas ähnliches in C# zu tun?

Mir ist klar, dass dies wahrscheinlich eine der Möglichkeiten ist, mit denen C# uns vor uns schützt, also suche ich nicht nach einem exakt gleichwertigen, sondern eher einem ähnlichen Konzept ... das heißt, darauf verweisen zu können der Inhalt einer anderen Variablen anstelle der Instanz der Variablen selbst.

Hier ist mein Anwendungsfall, an den ich denke. Ich habe eine Reihe von Saiten. Ich hätte gerne ein weiteres Array von Referenzen auf diese Array-Elemente. So etwas wie diese (natürlich nicht gültigen 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" 

Sicherlich ref paramets dies zu tun, und out-Parameter können Sie eine bestimmte Variable zu schreiben. Aber was ist mit Nichtparametern? Kannst du eine Referenz speichern? Kannst du eine Reihe von Referenzen oder ein Wörterbuch behalten?

Es scheint, als ob die Fähigkeit, es mit Parametern zu tun, vorhanden ist, sollte es einen Weg geben, es ohne sie zu tun.

+0

Ihre Frage scheint Referenzen mit Zeigern zu verwirren und verwendet einige ziemlich verwirrende Begriffe wie "den Inhalt" einer Variablen und "Variable selbst". 'int * p = & i [5]' weist die * Adresse * des fünften Elements des Feldes 'i' der Variablen 'p' zu, die als Zeiger auf ein 'int' deklariert ist. Verweise sind nicht die gleichen wie Zeiger. Ich bin mir nicht sicher über C#, aber aus einer C++ - Perspektive ist nicht klar, was Sie fragen. –

+0

@JamieBullock - Ich denke, es ist ziemlich klar, da alle anderen verstanden haben, worüber ich geredet habe. Verweise in C++ sind mehr oder weniger die gleichen wie Zeiger, außer dass Sie sie nicht mathematisch bearbeiten oder neu zuweisen können. Unabhängig davon sind C# -Referenzen nicht die gleichen wie C++ - Verweise, und C# hat kein Zeigerkonzept (wenn kein unsicherer Code verwendet wird), also sind Verweise das, was Sie in C# verwenden würden, da es sonst nichts gibt. Außerdem habe ich "Instanz der Variablen selbst" gesagt. –

Antwort

18

Nein. Durch Setzen von unsafe Code zur Seite, der es ermöglicht, Zeiger auf Speicherpositionen zu halten, gibt es keine Möglichkeit, einen Verweis auf eine Variable in C# zu speichern.

ref und out Argumente bieten die einzige Möglichkeit, eine Referenz zu nehmen, aber Sie können sie nirgendwo speichern.

Sie können diese Einschränkung umgehen, indem Sie Felder in eine class umhüllen und stattdessen deren Referenz verwenden. Dies ist, was der Compiler tut Variablen in Schließungen zu erfassen:

Zum Beispiel, wenn Sie schreiben:

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

In der zweiten Zeile wird der Compiler die anonyme Methode herausnehmen müssen, und speichern Sie es als eine separate Methode in der Klasse. Offensichtlich hat diese Methode keinen Zugriff auf integer Variable. Es muss irgendwie eine "Referenz" an integer Variable an diese anonyme Methode übergeben. Da dies nicht möglich ist, wird ein class mit einem Feld erzeugt, das eine Ganzzahl enthält, und eine Instanz dieser Klasse verwendet, um die Variable zu speichern. Im Grunde wird die lokale Variable zu einem Feld in einer Klasse befördert und im Heap gespeichert.

+0

Das ist ziemlich interessant und gibt mir ein paar Ideen. Vielen Dank. –

-1

ich Sie vermuten, dass die falsche Frage fragst ...

Betrachten Sie das folgende C# -Code:

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

In diesem Fall obj ist auf das gleiche Objekt beziehen wie arr [5 ], da sowohl arr [5] als auch obj Referenzen sind.

Das Problem mit dem von Ihnen bereitgestellten Beispielcode war, dass wenn Sie schreiben "s [1] =" zwei Punkt Null "" Sie tatsächlich die Zeichenfolge im Speicher ändern - Sie machen die Referenz im Array Zeigen Sie auf eine andere Zeichenfolge.Im Grunde ist die C# Code entspricht den folgend in 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" 
+0

Nein, ich stelle die richtige Frage. Ihr Punkt ist genau das, was ich vermeiden möchte. Eine Objektreferenz verweist auf eine Instanz der Daten, nicht auf die Position der Daten. –

+0

Aber das ist der Speicherort der Daten. Das String-Array ist nur der Ort der Referenz ... –

+0

Nein, du verstehst immer noch nicht. Ich schlage vor, dass Sie meine Frage und einige der Antworten, die ich gegeben habe, noch einmal durchlesen. Du erzählst mir etwas, was ich schon weiß. Ich weiß, obj bezieht sich auf das gleiche Objekt. Ich suche nach einer Möglichkeit, * NOT * auf die tatsächliche Instanz des Objekts zu verweisen, aber auf die Referenz selbst, genauso wie Ref-Parameter funktionieren. –

6

Eine Nur-Lese-Array-Referenz:

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" 
+0

Ich hatte über etwas in dieser Richtung nachgedacht, aber es kam mir nie richtig in den Sinn. Das gibt mir einige Ideen, zusammen mit Mehrdads Antwort. –

3

In verwalteten Code Referenzen verwendet werden anstelle von Zeigern, da die Garbage Collector bewegen kann Objekte im Speicher zu jedem Zeitpunkt.

Um einen Verweis auf etwas zu haben, muss es ein Objekt sein, so dass Sie keine Verweise auf die einzelnen Elemente in einem Integer-Array haben können. Als Strings Objekte sind, können Sie Verweise auf die einzelnen Strings haben, indem nur die Verweise in dem Array zu kopieren:

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

jedoch als Strings unveränderliche Objekte sind, können Sie nur die Verweise verwenden, um die Artikel zu lesen. Wenn Sie einer Referenz im Array "sr" eine Zeichenfolge zuweisen, überschreiben Sie die Referenz, anstatt das Objekt, auf das es verweist, zu ändern.

Wenn Sie die Objekte ändern möchten, müssen Sie veränderbare Objekte haben. Zum Beispiel:

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" 
+0

Ja, das ist ein interessanter Weg, es zu lösen. Nicht ganz, was ich vorhatte, aber eine mögliche Lösung. –

0

Wenn noch jemand nach einer möglichen Lösung sucht. Wenn Sie wenig mit Generics machen, ist es möglich, jedes Array in eine Klasse zu verpacken, so dass es einer anderen Instanz der gleichen Wrappklasse zugewiesen werden kann, indem ein Array darauf verweist.

Aber das sollte nur als Beweis für das Konzept verwendet werden, das möglich ist, es zu tun. Ich würde im Allgemeinen nicht empfehlen, dies zu verwenden, würde aber vorschlagen, den Code in effizienter Weise neu zu gestalten. Es ist auch erwähnenswert, dass Sie einfach Array als Verweis auf seine Elemente (DOH) einem anderen Array zuweisen können.

Das hier gesagt wird, ist der Beispielcode von generischer Referenzanordnung auf Arraydaten:

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

} 

Auch wenn referenzierten Elemente aus sein müssen, um Sie generische Ref_T Klasse hinzufügen könnten jeden Wert als Referenz zu wickeln:

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"); 
    } 

} 

Ausgänge:

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 jemand hilft.

Verwandte Themen