Suche…


Syntax

  • Übergeben als Referenz: public void Double (ref int numberToDouble) {}

Bemerkungen

Einführung

Werttypen

Werttypen sind die einfachere der beiden. Werttypen werden häufig verwendet, um Daten selbst darzustellen. Eine Ganzzahl, ein Boolean oder ein Punkt im 3D-Raum sind Beispiele für gute Werttypen.

Werttypen (Strukturen) werden mit dem Schlüsselwort struct deklariert. Ein Beispiel zur Deklaration einer neuen Struktur finden Sie im Abschnitt zur Syntax.

Im Allgemeinen verfügen wir über 2 Schlüsselwörter, mit denen Werttypen deklariert werden:

  • Structs
  • Aufzählungen

Referenztypen

Referenztypen sind etwas komplexer. Referenztypen sind traditionelle Objekte im Sinne der objektorientierten Programmierung. Sie unterstützen also die Vererbung (und deren Vorteile) und unterstützen Finalisierer.

In C # haben wir im Allgemeinen folgende Referenztypen:

  • Klassen
  • Delegierte
  • Schnittstellen

Neue Referenztypen (Klassen) werden mit dem Schlüsselwort class deklariert. Ein Beispiel finden Sie im Abschnitt zur Syntax, wie Sie einen neuen Referenztyp deklarieren.

Hauptunterschiede

Die wichtigsten Unterschiede zwischen Referenztypen und Werttypen sind unten zu sehen.

Werttypen sind auf dem Stack vorhanden, Referenztypen auf dem Heap

Dies ist der oft erwähnte Unterschied zwischen den beiden, aber eigentlich geht es darum, dass das Programm bei Verwendung eines Werttyps in C # wie einem int diese Variable verwendet, um sich direkt auf diesen Wert zu beziehen. Wenn Sie int mein = 0 sagen, bezieht sich die Variable mein direkt auf 0, was effizient ist. Referenztypen enthalten jedoch tatsächlich (wie der Name schon sagt) einen Verweis auf das zugrunde liegende Objekt. Dies ist vergleichbar mit Zeigern in anderen Sprachen wie C ++.

Sie werden die Auswirkungen möglicherweise nicht sofort bemerken, aber die Auswirkungen sind vorhanden, sind mächtig und subtil. Ein Beispiel finden Sie im Beispiel zum Ändern von Referenztypen an anderer Stelle.

Dieser Unterschied ist der Hauptgrund für die folgenden anderen Unterschiede und ist wissenswert.

Werttypen ändern sich nicht, wenn Sie sie in einer Methode ändern, Referenztypen

Wenn ein Werttyp als Parameter an eine Methode übergeben wird, ändert sich der Wert in keiner Weise durch die Methode. Der Wert wird jedoch nicht geändert. Wenn Sie jedoch einen Referenztyp an dieselbe Methode übergeben und ändern, ändert sich das zugrunde liegende Objekt Für andere Dinge, die dasselbe Objekt verwenden, wird das neu geänderte Objekt anstelle des ursprünglichen Werts angezeigt.

Weitere Informationen finden Sie im Beispiel für Werttypen und Referenztypen in Methoden.

Was ist, wenn ich sie ändern möchte?

Übergeben Sie sie einfach mit dem Schlüsselwort "ref" an Ihre Methode, und Sie übergeben dieses Objekt als Referenz. Das heißt, es ist das gleiche Objekt im Gedächtnis. Die von Ihnen vorgenommenen Änderungen werden daher respektiert. Ein Beispiel finden Sie im Beispiel zur Referenzübergabe.

Werttypen können nicht null sein, Referenztypen können dies tun

So wie es heißt, können Sie einem Referenztyp den Wert null zuweisen. Dies bedeutet, dass der Variablen, die Sie zugewiesen haben, kein Objekt zugewiesen werden kann. Bei Werttypen ist dies jedoch nicht möglich. Sie können jedoch Nullable verwenden, um zuzulassen, dass Ihr Werttyp auf Null gesetzt werden kann. Wenn dies eine Anforderung ist, sollten Sie jedoch bei starkem Nachdenken stark darüber nachdenken, ob eine Klasse hier möglicherweise nicht der beste Ansatz ist, wenn es Ihre eigene ist Art.

Werte an anderer Stelle ändern

public static void Main(string[] args)
{
    var studentList = new List<Student>();
    studentList.Add(new Student("Scott", "Nuke"));
    studentList.Add(new Student("Vincent", "King"));
    studentList.Add(new Student("Craig", "Bertt"));

    // make a separate list to print out later
    var printingList = studentList; // this is a new list object, but holding the same student objects inside it

    // oops, we've noticed typos in the names, so we fix those
    studentList[0].LastName = "Duke";
    studentList[1].LastName = "Kong";
    studentList[2].LastName = "Brett";

    // okay, we now print the list
    PrintPrintingList(printingList);
}

private static void PrintPrintingList(List<Student> students)
{
    foreach (Student student in students)
    {
        Console.WriteLine(string.Format("{0} {1}", student.FirstName, student.LastName));
    }
}

Sie werden feststellen, dass obwohl die Liste der Drucklisten vor den Korrekturen der Schülernamen nach dem Tippfehler erstellt wurde, die Methode PrintPrintingList immer noch die korrigierten Namen ausgibt:

Scott Duke
Vincent Kong
Craig Brett

Dies liegt daran, dass beide Listen eine Liste von Verweisen auf dieselben Schüler enthalten. Das Ändern des zugrundeliegenden Studentenobjekts führt zu einer Verwendung durch eine der beiden Listen.

So würde die Studentenklasse aussehen.

public class Student
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Student(string firstName, string lastName)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
    }
}

Übergabe als Referenz

Wenn das Beispiel für Wertetypen im Vergleich zu Referenztypen ordnungsgemäß funktionieren soll, verwenden Sie das Schlüsselwort ref in Ihrer Methodensignatur für den Parameter, den Sie als Referenz übergeben möchten, sowie beim Aufrufen der Methode.

public static void Main(string[] args)
{
    ...
    DoubleNumber(ref number); // calling code
    Console.WriteLine(number); // outputs 8
    ...
}
public void DoubleNumber(ref int number)
{
    number += number;
}

Wenn Sie diese Änderungen vornehmen, wird die Anzahl wie erwartet aktualisiert. Dies bedeutet, dass die Konsolenausgabe für number 8 wäre.

Übergabe als Referenz mit dem Schlüsselwort ref.

Aus der Dokumentation :

In C # können Argumente entweder per Wert oder als Referenz an Parameter übergeben werden. Durch das Übergeben als Referenz können Funktionsmitglieder, Methoden, Eigenschaften, Indexer, Operatoren und Konstruktoren den Wert der Parameter ändern und diese Änderung in der aufrufenden Umgebung beibehalten. Um einen Parameter als Referenz zu übergeben, verwenden Sie das Schlüsselwort ref oder out .

Der Unterschied zwischen ref und out ist , dass out bedeutet , dass der übergebene Parameter zugeordnet werden muss , bevor die Funktion ends.in Kontrastparameter mit geben ref geändert werden oder unverändert gelassen.

using System;

class Program
{
    static void Main(string[] args)
    {
        int a = 20;
        Console.WriteLine("Inside Main - Before Callee: a = {0}", a);
        Callee(a);
        Console.WriteLine("Inside Main - After Callee: a = {0}", a);
        
        Console.WriteLine("Inside Main - Before CalleeRef: a = {0}", a);
        CalleeRef(ref a);
        Console.WriteLine("Inside Main - After CalleeRef: a = {0}", a);
     
        Console.WriteLine("Inside Main - Before CalleeOut: a = {0}", a);
        CalleeOut(out a);
        Console.WriteLine("Inside Main - After CalleeOut: a = {0}", a);
        
        Console.ReadLine();
    }

    static void Callee(int a)
    {
        a = 5;
        Console.WriteLine("Inside Callee a : {0}", a);
    }

    static void CalleeRef(ref int a)
    {
        a = 6;
        Console.WriteLine("Inside CalleeRef a : {0}", a);
    }
    
    static void CalleeOut(out int a)
    {
        a = 7;
        Console.WriteLine("Inside CalleeOut a : {0}", a);
    }
}

Ausgabe :

Inside Main - Before Callee: a = 20
Inside Callee a : 5
Inside Main - After Callee: a = 20
Inside Main - Before CalleeRef: a = 20
Inside CalleeRef a : 6
Inside Main - After CalleeRef: a = 6
Inside Main - Before CalleeOut: a = 6
Inside CalleeOut a : 7
Inside Main - After CalleeOut: a = 7

Zuordnung

var a = new List<int>();
var b = a;
a.Add(5);
Console.WriteLine(a.Count); // prints 1 
Console.WriteLine(b.Count); // prints 1 as well

Durch Zuweisen einer Variablen einer List<int> wird keine Kopie der List<int> . Stattdessen wird der Verweis in die List<int> kopiert. Wir nennen Typen, die sich als Referenztypen verhalten.

Unterschied bei den Methodenparametern ref und out

Es gibt zwei Möglichkeiten, einen Werttyp als Referenz zu übergeben: ref und out . Der Unterschied besteht darin, dass der Wert durch Übergeben mit ref initialisiert werden muss, nicht jedoch, wenn er nicht mitgegeben out . Durch out Verwendung von out sichergestellt, dass die Variable nach dem Methodenaufruf einen Wert hat:

public void ByRef(ref int value)
{
    Console.WriteLine(nameof(ByRef) + value);
    value += 4;
    Console.WriteLine(nameof(ByRef) + value);
}

public void ByOut(out int value)
{
    value += 4 // CS0269: Use of unassigned out parameter `value'  
    Console.WriteLine(nameof(ByOut) + value); // CS0269: Use of unassigned out parameter `value'  

    value = 4;
    Console.WriteLine(nameof(ByOut) + value);
}

public void TestOut()
{
    int outValue1;
    ByOut(out outValue1); // prints 4

    int outValue2 = 10;   // does not make any sense for out
    ByOut(out outValue2); // prints 4
}

public void TestRef()
{
    int refValue1;
    ByRef(ref refValue1); // S0165  Use of unassigned local variable 'refValue'

    int refValue2 = 0;
    ByRef(ref refValue2); // prints 0 and 4

    int refValue3 = 10;
    ByRef(ref refValue3); // prints 10 and 14
}

Der Haken ist, dass der Parameter durch Verwendung von out initialisiert werden must , bevor die Methode verlassen wird. Daher ist die folgende Methode mit ref jedoch nicht ohne out :

public void EmtyRef(bool condition, ref int value)
{
    if (condition)
    {
        value += 10;
    }
}

public void EmtyOut(bool condition, out int value)
{
    if (condition)
    {
        value = 10;
    }
} //CS0177: The out parameter 'value' must be assigned before control leaves the current method

Dies liegt daran, dass der value nicht zugewiesen wird, wenn die condition nicht gilt.

ref vs out-Parameter

Code

class Program
{
    static void Main(string[] args)
    {
        int a = 20;
        Console.WriteLine("Inside Main - Before Callee: a = {0}", a);
        Callee(a);
        Console.WriteLine("Inside Main - After Callee: a = {0}", a);
        Console.WriteLine();

        Console.WriteLine("Inside Main - Before CalleeRef: a = {0}", a);
        CalleeRef(ref a);
        Console.WriteLine("Inside Main - After CalleeRef: a = {0}", a);
        Console.WriteLine();

        Console.WriteLine("Inside Main - Before CalleeOut: a = {0}", a);
        CalleeOut(out a);
        Console.WriteLine("Inside Main - After CalleeOut: a = {0}", a);
        Console.ReadLine();
    }

    static void Callee(int a)
    {
        a += 5;
        Console.WriteLine("Inside Callee a : {0}", a);
    }

    static void CalleeRef(ref int a)
    {
        a += 10;
        Console.WriteLine("Inside CalleeRef a : {0}", a);
    }

    static void CalleeOut(out int a)
    {
        // can't use a+=15 since for this method 'a' is not intialized only declared in the method declaration
        a = 25; //has to be initialized
        Console.WriteLine("Inside CalleeOut a : {0}", a);
    }
}

Ausgabe

Inside Main - Before Callee: a = 20
Inside Callee a : 25
Inside Main - After Callee: a = 20

Inside Main - Before CalleeRef: a = 20
Inside CalleeRef a : 30
Inside Main - After CalleeRef: a = 30

Inside Main - Before CalleeOut: a = 30
Inside CalleeOut a : 25
Inside Main - After CalleeOut: a = 25


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow