web-dev-qa-db-ja.com

C#で参照または値によるオブジェクトの受け渡し

C#では、非プリミティブ変数は参照によって渡され、プリミティブ値は値によって渡されると常に考えていました。

そのため、任意の非プリミティブオブジェクトをメソッドに渡すと、そのメソッド内のオブジェクトに対して行われたものはすべて、渡されるオブジェクトに影響を与えます。 (C#101のもの)

しかし、System.Drawing.Imageオブジェクトを渡すと、そうではないことに気付きました。 system.drawing.imageオブジェクトを別のメソッドに渡してそのオブジェクトにイメージをロードした場合、そのメソッドはスコープ外に出て呼び出し元のメソッドに戻ると、そのイメージは元のオブジェクトにロードされません

どうしてこれなの?

199
michael

オブジェクトはまったく渡されません。デフォルトでは、引数は評価され、そのが、呼び出すメソッドのパラメータの初期値として値渡しされます。重要な点は、値が参照型の参照、つまりオブジェクトに到達する方法(またはnull)であることです。そのオブジェクトへの変更は呼び出し元から見えます。ただし、別のオブジェクトを参照するようにパラメータの値を変更しても、値渡しを使用している場合は表示されません すべてのタイプ。

参照渡しを使用する場合は、パラメータの型が値型か参照型かにかかわらず、outまたはrefを使用する必要があります。その場合、事実上変数自体が参照によって渡されるので、パラメータは引数と同じ格納場所を使用します - そしてパラメータ自体への変更は呼び出し側から見られます。

そう:

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(...);
}

私は これについてはもっと詳しく述べる記事 を持っています。基本的に、「参照渡し」とは、あなたがそれを意味していると考える意味ではありません。

422
Jon Skeet

これを紹介するもう1つのコードサンプル:

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

そして出力:

テストプレーン:0

TestRef:5

TestObjPlain:テスト

TestObjRef:TestObjRef

18
vmg

このようにすると、より明確になると思います。このようなことをテストするには、 LinqPad をダウンロードすることをお勧めします。

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

そしてそれは出力されるはずです

WontUpdate

名前:Egli、名字:Becerra

UpdateImplicitly

姓:Favio、姓:Becerra

UpdateExplicitly

姓:Favio、姓:Becerra

5
Egli Becerra

System.Drawing.Image型のオブジェクトをメソッドに渡すと、実際にはそのオブジェクトへの参照のコピーが渡されます。

そのため、そのメソッド内で新しい画像をロードしている場合は、新しい参照またはコピーされた参照を使用してロードしています。あなたはオリジナルに変更を加えていません。

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

どのようにオブジェクトをメソッドに渡しましたか?

あなたはオブジェクトのためにそのメソッドの内部で新しいことをしていますか?もしそうなら、methodでrefを使う必要があります。

次のリンクはあなたに良い考えを与えます。

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

2
dotnetstep