web-dev-qa-db-de.com

Objekte als Referenz oder Wert in C # übergeben

In C # habe ich immer gedacht, dass nicht-primitive Variablen als Referenz und primitive Werte als Wert übergeben wurden.

Wenn also ein nicht-primitives Objekt an eine Methode übergeben wird, wirkt sich alles, was mit dem Objekt in der Methode gemacht wird, auf das übergebene Objekt aus. (C # 101 Zeug)

Ich habe jedoch festgestellt, dass dies nicht der Fall zu sein scheint, wenn ich ein System.Drawing.Image-Objekt übergebe. Wenn ich ein system.drawing.image-Objekt an eine andere Methode übergebe und ein Bild auf dieses Objekt lade, diese Methode den Gültigkeitsbereich verlässt und zur aufrufenden Methode zurückkehrt, wird dieses Bild nicht auf das ursprüngliche Objekt geladen?

Warum ist das?

209
michael

Objekte werden überhaupt nicht übergeben. Standardmäßig wird das Argument ausgewertet und dessen Wert als Anfangswert des Parameters der aufgerufenen Methode übergeben. Nun ist der wichtige Punkt, dass der Wert eine Referenz für Referenztypen ist - eine Möglichkeit, zu einem Objekt (oder zu null) zu gelangen. Änderungen an diesem Objekt sind für den Aufrufer sichtbar. Wenn Sie jedoch den Wert des Parameters ändern, um auf ein anderes Objekt zu verweisen, wird nicht angezeigt, wenn Sie den Übergabewert verwenden. Dies ist die Standardeinstellung für alle = Typen.

Wenn Sie eine Referenzübergabe verwenden möchten, verwenden Sie mussout oder ref, unabhängig davon, ob der Parametertyp ein Werttyp oder ein Referenztyp ist. In diesem Fall wird die Variable selbst als Referenz übergeben, sodass der Parameter denselben Speicherort wie das Argument verwendet und Änderungen am Parameter selbst vom Aufrufer gesehen werden.

Damit:

public void Foo(Image image)
{
    // This change won't be seen by the caller: it's changing the value
    // of the parameter.
    image = Image.FromStream(...);
}

public void Foo(ref Image image)
{
    // This change *will* be seen by the caller: it's changing the value
    // of the parameter, but we're using pass by reference
    image = Image.FromStream(...);
}

public void Foo(Image image)
{
    // This change *will* be seen by the caller: it's changing the data
    // within the object that the parameter value refers to.
    image.RotateFlip(...);
}

Ich habe einen Artikel, auf den hier viel näher eingegangen wird . Grundsätzlich bedeutet "Referenzübergabe" nicht, was Sie denken, dass es bedeutet.

439
Jon Skeet

Ein weiteres Codebeispiel, um dies zu demonstrieren:

void Main()
{


    int k = 0;
    TestPlain(k);
    Console.WriteLine("TestPlain:" + k);

    TestRef(ref k);
    Console.WriteLine("TestRef:" + k);

    string t = "test";

    TestObjPlain(t);
    Console.WriteLine("TestObjPlain:" +t);

    TestObjRef(ref t);
    Console.WriteLine("TestObjRef:" + t);
}

public static void TestRef(ref int i)
{
    i = 5;
}

public  static void TestPlain(int i)
{
    i = 5;
}

public static void TestObjRef(ref string s)
{
    s = "TestObjRef";
}

public static void TestObjPlain(string s)
{
    s = "TestObjPlain";
}

Und die Ausgabe:

TestPlain: 0

TestRef: 5

TestObjPlain: Test

TestObjRef: TestObjRef

18
vmg

Ich denke, es ist klarer, wenn du es so machst. Ich empfehle LinqPad herunterzuladen, um solche Dinge zu testen.

void Main()
{
    var Person = new Person(){FirstName = "Egli", LastName = "Becerra"};

    //Will update egli
    WontUpdate(Person);
    Console.WriteLine("WontUpdate");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateImplicitly(Person);
    Console.WriteLine("UpdateImplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateExplicitly(ref Person);
    Console.WriteLine("UpdateExplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");
}

//Class to test
public class Person{
    public string FirstName {get; set;}
    public string LastName {get; set;}

    public string printName(){
        return $"First name: {FirstName} Last name:{LastName}";
    }
}

public static void WontUpdate(Person p)
{
    //New instance does jack...
    var newP = new Person(){FirstName = p.FirstName, LastName = p.LastName};
    newP.FirstName = "Favio";
    newP.LastName = "Becerra";
}

public static void UpdateImplicitly(Person p)
{
    //Passing by reference implicitly
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

public static void UpdateExplicitly(ref Person p)
{
    //Again passing by reference explicitly (reduntant)
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

Und das sollte ausgeben

WontUpdate

Vorname: Egli, Nachname: Becerra

UpdateImplicitly

Vorname: Favio, Nachname: Becerra

UpdateExplicitly

Vorname: Favio, Nachname: Becerra

6
Egli Becerra

Wenn Sie das Objekt vom Typ System.Drawing.Image An eine Methode übergeben, übergeben Sie tatsächlich eine Kopie des Verweises auf dieses Objekt.

Wenn Sie also innerhalb dieser Methode ein neues Bild laden, verwenden Sie eine neue/kopierte Referenz. Sie nehmen keine Änderungen im Original vor.

YourMethod(System.Drawing.Image image)
{
    //now this image is a new reference
    //if you load a new image 
    image = new Image()..
    //you are not changing the original reference you are just changing the copy of original reference
}
3
Haris Hasan

Wie haben Sie das Objekt an die Methode übergeben?

Machst du neu in dieser Methode für Objekt? In diesem Fall müssen Sie ref in der Methode verwenden.

Der folgende Link gibt Ihnen eine bessere Vorstellung.

http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html

2
dotnetstep