객체를 전달할 때 왜 'ref'키워드를 사용합니까?


290

객체를 메소드에 전달하는 경우 왜 ref 키워드를 사용해야합니까? 어쨌든 이것이 기본 동작이 아닙니까?

예를 들면 다음과 같습니다.

class Program
{
    static void Main(string[] args)
    {
        TestRef t = new TestRef();
        t.Something = "Foo";

        DoSomething(t);
        Console.WriteLine(t.Something);
    }

    static public void DoSomething(TestRef t)
    {
        t.Something = "Bar";
    }
}


public class TestRef
{
    public string Something { get; set; }
}

출력은 "Bar"입니다. 이는 객체가 참조로 전달되었음을 의미합니다.

답변:


299

ref객체가 무엇인지 변경하려면 a를 전달 하십시오.

TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef t)
{
  t = new TestRef();
  t.Something = "Not just a changed t, but a completely different TestRef object";
}

DoSomething을 호출 한 후 toriginal를 참조하는 것이 new TestRef아니라 완전히 다른 객체를 나타냅니다.

변경할 수없는 객체의 값을 변경하려는 경우에도 유용합니다 (예 : a) string. string일단 작성된 값은 변경할 수 없습니다 . 그러나를 사용하면 ref값이 다른 다른 문자열을 변경하는 함수를 만들 수 있습니다.

편집 : 다른 사람들이 언급했듯이. ref필요한 경우가 아니면 사용 하지 않는 것이 좋습니다 . 를 사용 ref하면 메소드가 다른 것에 대한 인수를 자유롭게 변경할 수 있으므로 메소드 호출자는 이러한 가능성을 처리하도록 코딩해야합니다.

또한 매개 변수 유형이 객체 인 경우 객체 변수는 항상 객체에 대한 참조로 작동합니다. 이는 ref키워드가 사용될 때 참조에 대한 참조를 얻었음을 의미합니다 . 이를 통해 위에 제공된 예에 설명 된대로 작업을 수행 할 수 있습니다. 그러나 매개 변수 유형이 기본 값 (예 :) 인 int경우이 매개 변수가 메소드 내에 지정되면 전달 된 인수의 값은 메소드가 리턴 한 후에 변경됩니다.

int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10

void Change(ref int x)
{
  x = 5;
}

void WillNotChange(int x)
{
  x = 10;
}

88

"값으로 참조 전달"과 "참조로 매개 변수 / 인수 전달"을 구별해야합니다.

나는 뉴스 그룹에 올 때마다주의 깊게 글을 쓰지 않기 위해 주제에 대해 상당히 긴 기사 를 작성했습니다. :)


1
VB6을 .Net C # 코드로 업그레이드하는 동안 문제가 발생했습니다. ref, out 및 일반 매개 변수를 취하는 기능 / 방법 서명이 있습니다. 그렇다면 일반 매개 변수와 참조 매개 변수의 차이점을 어떻게 더 잘 구별 할 수 있습니까?
bonCodigo

2
@bonCodigo : "더 나은 구별"의 의미가 무엇인지 확실하지 않습니다. 서명의 일부이며 ref, 호출 사이트에서도 지정해야 합니다. 시맨틱도 상당히 명확하지만 신중하게 표현해야합니다 (일반적인과 단순화 인 "객체가 참조로 전달됨"대신).
Jon Skeet

비주얼 스튜디오는 여전히 쇼를 나던 내가 왜 전달됩니다 명시 적으로 무엇인지 잘 모릅니다
MonsterMMORPG

3
@MonsterMMORPG : 그게 무슨 뜻인지 모르겠습니다.
Jon Skeet

57

.NET에서 매개 변수를 메소드에 전달하면 사본이 작성됩니다. 값 형식에서 값을 수정하면 메서드 범위에 있고 메서드를 종료하면 손실됩니다.

참조 유형을 전달할 때 사본도 작성되지만 참조의 사본입니다. 즉, 동일한 오브젝트에 대한 메모리에 두 개의 참조가 있습니다. 따라서 참조를 사용하여 객체를 수정하면 수정됩니다. 그러나 참조 자체를 수정하면 참조가 사본임을 기억해야합니다. 그러면 메소드를 종료 할 때 모든 변경 사항도 손실됩니다.

사람들이 이전에 언급했듯이 과제는 참조의 수정이므로 손실됩니다.

public void Method1(object obj) {   
 obj = new Object(); 
}

public void Method2(object obj) {  
 obj = _privateObject; 
}

위의 방법은 원본 객체를 수정하지 않습니다.

예제의 약간의 수정

 using System;

    class Program
        {
            static void Main(string[] args)
            {
                TestRef t = new TestRef();
                t.Something = "Foo";

                DoSomething(t);
                Console.WriteLine(t.Something);

            }

            static public void DoSomething(TestRef t)
            {
                t = new TestRef();
                t.Something = "Bar";
            }
        }



    public class TestRef
    {
    private string s;
        public string Something 
        { 
            get {return s;} 
            set { s = value; }
        }
    }

6
나는이 대답이 수용 된 대답보다 더 좋아. ref 키워드와 함께 참조 유형 변수를 전달할 때 발생하는 상황을보다 명확하게 설명합니다. 감사합니다!
Stefan

17

TestRef는 클래스 (참조 객체)이기 때문에 참조로 전달하지 않고 t 내부의 내용을 변경할 수 있습니다. 그러나 t를 참조로 전달하면 TestRef가 원래 t가 참조하는 것을 변경할 수 있습니다. 즉, 다른 객체를 가리 키도록합니다.


16

ref당신 과 함께 쓸 수 있습니다 :

static public void DoSomething(ref TestRef t)
{
    t = new TestRef();
}

그리고 방법이 완료되면 t가 변경됩니다.


8

foo참조 유형 (예 :)의 변수 (예 :)를 "개체 # 24601"형식의 객체 식별자List<T>보유하는 것으로 생각하십시오 . 명령문 이 "개체 # 24601"(4 개의 항목이있는 목록)을 보유 한다고 가정하십시오 . 그런 다음 호출 하면 객체 # 24601에 길이가 표시되고 4가 응답하므로 4가됩니다.foo = new List<int> {1,5,7,9};foofoo.Lengthfoo.Length

경우 foo사용하지 않고 메서드에 전달되어 ref, 그 방법은 # 24601을 Object로 변경할 수 있습니다. 이러한 변경의 결과로 foo.Length더 이상 4와 같지 않을 수 있습니다. 그러나 메소드 자체는 변경할 수 없으며 foo"Object # 24601"을 계속 보유합니다.

매개 변수 foo로 전달 ref하면 호출 된 메서드가 개체 # 24601뿐만 아니라 foo자체 도 변경할 수 있습니다. 이 메소드는 새 객체 # 8675309를 생성하고에 대한 참조를에 저장할 수 있습니다 foo. 그렇게 foo하면 더 이상 "개체 # 24601"이 아니라 "개체 # 8675309"가됩니다.

실제로, 참조 형 변수는 "Object # 8675309"형식의 문자열을 보유하지 않습니다. 그들은 의미있게 숫자로 변환 될 수있는 것도 가지고 있지 않습니다. 각 참조 유형 변수는 약간의 비트 패턴을 보유하지만 이러한 변수에 저장된 비트 패턴과 이들이 식별하는 객체 사이에는 고정 된 관계가 없습니다. 코드가 객체 또는 객체에 대한 참조에서 정보를 추출 할 수있는 방법은 없으며 나중에 코드가 원래 객체를 식별하는 참조를 보유하거나 알지 않는 한 다른 참조가 동일한 객체를 식별했는지 여부를 결정합니다.


5

이것은 C에서 포인터로 포인터를 전달하는 것과 같습니다. .NET에서는 원래 T가 참조하는 것을 개인적 으로 변경할 수 있습니다. 으로 .NET에서 그렇게하면 디자인 문제가 있다고 생각합니다!


3

ref참조 유형에 키워드 를 사용하면 참조에 효과적으로 참조를 전달하게됩니다. 여러면에서 out키워드 를 사용하는 것과 동일 하지만 약간의 차이는 있지만 메소드가 실제로 ref'ed 매개 변수에 무엇이든 할당한다는 보장은 없습니다 .


3

ref 두 가지 범위의 전역 영역으로 모방 (또는 행동) :

  • 방문객
  • 수신자

1

그러나 값을 전달하면 상황이 다릅니다. 값을 참조로 전달하도록 할 수 있습니다. 이를 통해 예를 들어 메소드에 정수를 전달하고 메소드가 사용자를 대신하여 정수를 수정하도록 할 수 있습니다.


4
참조를 전달하든 값 유형을 전달하든 기본 동작은 값을 전달하는 것입니다. 당신은 가치 당신이있는 거 통과는 참조 유형과 그를 이해할 필요가 있다 참조. 이는 전달과 동일하지 의해 참조.
Jon Skeet

1

Ref는 함수가 객체 자체에 손을 댈 수 있는지 아니면 그 값에만 손을 댈 수 있는지 나타냅니다.

참조에 의한 전달은 언어에 구속되지 않습니다. 값으로 전달, 이름으로 전달, 필요로 전달 등의 매개 변수 바인딩 전략입니다.

참고 사항 : 클래스 이름 TestRef은이 맥락에서 엄청나게 나쁜 선택입니다.).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.