C #에서 참조 또는 값으로 객체 전달


233

C #에서 필자는 기본이 아닌 변수는 참조로 전달되고 기본 값은 값으로 전달된다고 생각했습니다.

따라서 기본이 아닌 객체를 메소드에 전달할 때 메소드의 객체에 수행 된 모든 작업은 객체가 전달되는 데 영향을 미칩니다. (C # 101 물건)

그러나 System.Drawing.Image 개체를 전달할 때 이것이 그렇지 않은 것으로 나타났습니다. system.drawing.image 객체를 다른 메소드에 전달하고 해당 객체에 이미지를로드 한 다음 해당 메소드가 범위를 벗어나 호출 메소드로 돌아 가면 해당 이미지가 원래 객체에로드되지 않습니까?

왜 이런거야?


20
모든 변수는 기본적으로 C #에서 값으로 전달됩니다. 참조 유형의 경우 참조 값을 전달 합니다 .
Andrew Barber

같은 것 :
그렇다

답변:


502

객체 는 전혀 전달되지 않습니다. 기본적으로 인수는 평가되고 해당 은 호출하는 메서드 매개 변수의 초기 값으로 값별로 전달됩니다. 이제 중요한 점은 값이 참조 유형에 대한 참조-객체에 도달하는 방법 (또는 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(...);
}

나는 이것에 훨씬 더 많은 기사를 가지고있다 . 기본적으로 "참조로 전달"이 의미하는 바를 의미하지는 않습니다.


2
당신의 권리, 나는 그것을 보지 못했다! 이미지 = Image.FromFile (..)을로드하고 변수 이미지를 바꾸고 객체를 변경하지 않았습니다! :) 물론이야.
michael

1
@Adeem : 확실하지 않음- "매개 변수 개체"가 없으며 매개 변수 값이 참조하는 개체가 있습니다. 나는 당신이 바로 생각이있어 생각하지만, 용어 : 중요
존 소총

2
우리가 키워드를 떨어 뜨린 경우 refoutC #을에서를, 그것은 확인 자바는 값으로 즉, 언제나처럼 그 C #을 매개 변수 같은 방법으로 전달 말을하는 것입니다. Java와의 차이점이 있습니까?
광대역

1
@broadband : 예. 기본 통과 모드는 값별입니다. 물론 C #에는 포인터와 사용자 정의 값 유형이 있지만 Java보다 조금 더 복잡합니다.
Jon Skeet 2012 년

3
@Vippy : 아뇨, 전혀 아닙니다. 참조 의 사본입니다 . 링크 된 기사를 읽는 것이 좋습니다.
Jon Skeet

18

이것을 보여주는 또 하나의 코드 샘플 :

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 TestPlain(int i)
{
    i = 5;
}

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

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

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

그리고 출력 :

TestPlain : 0

TestRef : 5

TestObjPlain : 테스트

TestObjRef : 테스트 ObjRef


2
따라서 기본적으로 참조 유형은 발신자 기능의 변경 사항을 보려면 참조로 전달해야합니다.
깨지지 않는

1
문자열은 변경할 수없는 참조 유형입니다. 불변이란, 작성된 후에는 변경할 수 없음을 의미합니다. 문자열을 변경할 때마다 새 문자열이 만들어집니다. 이것이 호출 메소드를 변경하기 위해 문자열을 'ref'로 전달해야하는 이유입니다. 호출 메소드에서 변경 사항을 다시 가져 오기 위해 다른 오브젝트 (예 : 직원)를 'ref'없이 전달할 수 있습니다.
히말라야 Garg

1
@almg, HimalayaGarg에 따르면 이것은 좋은 예가 아닙니다. 변경할 수없는 다른 참조 유형 예제를 포함해야합니다.
다니엘

11

좋은 답변이 많이 추가되었습니다. 나는 여전히 공헌하고 싶습니다. 약간 더 명확해질 것입니다.

인스턴스를 메소드에 인수로 전달하면 인스턴스의 인스턴스를 전달합니다 copy. 이제 전달하는 인스턴스가 value type( stack)에 있으면 해당 값 의 사본 을 전달 하므로 수정하면 호출자에게 반영되지 않습니다. 인스턴스가 참조 유형 인 경우 참조 사본 ( stack)이 객체에 전달됩니다. 따라서 동일한 객체에 대한 두 개의 참조가 있습니다. 둘 다 객체를 수정할 수 있습니다. 그러나 메소드 본문 내에서 새 오브젝트를 인스턴스화하면 참조 사본이 더 이상 원래 오브젝트를 참조하지 않고 방금 작성한 새 오브젝트를 참조합니다. 따라서 2 개의 참조와 2 개의 객체가 생깁니다.


이것이 정답입니다!

나는 완전히 동의한다! :)
JOSEFtw

8

당신이 이렇게하면 더 명확하다고 생각합니다. 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

암시 적으로 업데이트

이름 : Favio, 성 : Becerra

명시 적으로 업데이트

이름 : Favio, 성 : Becerra


public static void WhatAbout (Person p) {p = new Person () {FirstName = "First", LastName = "Last"}; }. :)
마린 포포프

4

당신이 통과 할 때 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
}


-1

Pass By Reference에서 함수 매개 변수에는 "ref"만 추가하고 main 때문에 static (# public void main(String[] args)) 때문에 함수 "static"을 선언해야하는 것이 한 가지 더 있습니다 !

namespace preparation
{
  public  class Program
    {
      public static void swap(ref int lhs,ref int rhs)
      {
          int temp = lhs;
          lhs = rhs;
          rhs = temp;
      }
          static void Main(string[] args)
        {
            int a = 10;
            int b = 80;

  Console.WriteLine("a is before sort " + a);
            Console.WriteLine("b is before sort " + b);
            swap(ref a, ref b);
            Console.WriteLine("");
            Console.WriteLine("a is after sort " + a);
            Console.WriteLine("b is after sort " + b);  
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.