'ref'와 'out'키워드의 차이점은 무엇입니까?


891

함수에 의해 수정 될 수 있도록 객체를 전달해야하는 함수를 만들고 있습니다. 차이점은 무엇입니까?

public void myFunction(ref MyClass someClass)

public void myFunction(out MyClass someClass)

어느 것을 사용해야하며 왜 그런가요?


69
당신 : 나는 그것을 수정 될 수 있도록 객체를 전달해야 처럼 보이는 MyClassclass즉, 참조 형식을 입력합니다. 이 경우 전달하는 객체 myFunctionref/ out키워드 없이 짝수 로 수정할 수 있습니다 . 동일한 객체 를 가리키는 새로운 참조를 myFunction수신하고 원하는만큼 동일한 객체를 수정할 수 있습니다. 키워드와 의 차이점은 동일한 객체에 대한 동일한 참조를 받았다는 것 입니다. 다른 객체 를 가리 키도록 참조를 변경하는 경우에만 중요 합니다. refmyFunctionmyFunction
Jeppe Stig Nielsen

3
@AnthonyKolesov가 완벽 할 때 혼란스러워하는 대답에 의아해합니다.
o0 '.

out 메소드 선언은 메소드가 여러 값을 리턴하도록하려는 경우에 유용합니다. 하나의 인수를 널에 지정할 수 있습니다. 이를 통해 메소드가 값을 선택적으로 리턴 할 수 있습니다.
Yevgraf Andreyevich Zhivago 2

여기에 예제로 설명되어 있습니다. 더 이해하기 쉽습니다
Prageeth godage

2
@JeppeStigNielsen의 의견은 기술적으로 OP의 실제 질문에 대한 (답) 정답입니다. 메소드가 오브젝트를 수정할 수 있도록 메소드에 오브젝트를 전달하려면 값으로 메소드에 (참조를) 참조하십시오. 메소드가 자체의 개별 변수 (동일한 오브젝트를 참조)를 포함하더라도 오브젝트 인수를 통해 메소드 내에서 오브젝트를 변경하면 원래 오브젝트가 수정 됩니다.
David R Tribble

답변:


1160

ref함수에 들어가기 전에 객체가 초기화 out되었음을 컴파일러에 알리고 함수 내부에서 객체가 초기화 될 것을 컴파일러에 지시합니다.

동안은 그래서 ref두 가지 방법 인, out아웃 전용입니다.


270
out과 관련된 또 다른 멋진 점은 함수가 out 매개 변수에 할당해야한다는 것입니다. 할당되지 않은 채로 둘 수 없습니다.
Daniel Earwicker

7
'ref'는 값 유형에만 적용됩니까? 참조 유형은 항상 ref로 전달되므로
결함

3
예. 구조체를 포함하는 값 유형
룬 Grimstad의

17
@faulty : 아니요. ref는 값 유형에만 적용되는 것은 아닙니다. ref / out은 C / C ++의 포인터와 같으며 직접 객체 대신 객체의 메모리 위치 (C #에서 간접적으로)를 처리합니다.
THR

52
@faulty : 반 직관적으로, 참조 유형은 참조 지정자를 사용하지 않는 한 항상 C #의 값으로 전달됩니다. myval = somenewval을 설정하면 효과는 해당 기능 범위에만 있습니다. ref 키워드를 사용하면 myval이 somenewval을 가리 키도록 변경할 수 있습니다.
JasonTrue

535

ref수정 수단 :

  1. 값이 이미 설정되어 있으며
  2. 메소드는이를 읽고 수정할 수 있습니다.

out수정 수단 :

  1. 값이 설정되지 않고 설정 될 때까지 메소드가 읽을 수 없습니다 .
  2. 메소드 리턴하기 전에 설정 해야합니다 .

30
이 답변은 ref 키워드가 아닌 out 키워드를 사용할 때 컴파일러가 부과하는 제한 사항을 가장 명확하고 간결하게 설명합니다.
윌리 박사 견습생

5
MSDN에서 : ref 매개 변수는 사용하기 전에 초기화해야하지만 out 매개 변수는 전달되기 전에 명시 적으로 초기화 할 필요가 없으며 이전 값은 무시됩니다.
시바 쿠마

1
을 사용 out하면 메소드가 호출되기 전에 초기화 된 경우 해당 메소드에 의해 설정되기 전에 메소드 내에서 전혀 읽을 수 있습니까? 내 말은, 호출 된 메소드가 호출 메소드가 인수로 전달한 것을 읽을 수 있습니까?
Panzercrisis

3
Panzercrisis는 "out"에 대해 호출 된 메소드가 이미 설정된 경우 읽을 수 있습니다. 그러나 다시 설정해야합니다.
robert jebakumar2

146

Dom이 Peter의 칸막이에 TPS 보고서에 대한 메모에 대해 등장한다고 가정 해 봅시다.

Dom이 심판 주장이라면, 그는 메모 사본을 인쇄했을 것입니다.

Dom이 논쟁의 여지가 없다면, Peter는 그와 함께 가져갈 메모의 새 사본을 인쇄하게 할 것입니다.


54
심판은 피터가 보고서를 수정할 수 있도록 연필로 보고서를 작성했을 것입니다
Deebster

6
@Deebster 당신은 알고있다. 은유는 ​​결코 당신에게 아무것도하지 않았다. 왜 그렇게 고문해야 하는가? ;)
Michael Blackburn

21
아직 엔터테인먼트 교육은 유래 이와 비슷한 더 많은 게시물 필요
프랭크 Visaggio

2
누군가이 답변이 절반 만 웃기는 경우를 대비하여 영화 "Office Space"를보십시오.
displayName

그리고 Dom과 Peters의 사장님은 Dom (논쟁으로) 아래에 서서, Peter가 Domd에게 인쇄물을 넘겨 줄 때까지 두 가지 모두를 인쇄하도록 강요했습니다
Patrick Artner

57

나는 설명을 위해 내 손을 시험해 볼 것이다.

가치 유형이 올바르게 작동하는 방식을 이해하고 있다고 생각합니까? 값 유형은 (int, long, struct 등)입니다. ref 명령없이 함수로 보내면 데이터가 복사 됩니다 . 함수에서 해당 데이터에 대한 모든 작업은 원본이 아닌 사본에만 영향을 미칩니다. ref 명령은 ACTUAL 데이터를 보내고 변경 사항은 함수 외부의 데이터에 영향을줍니다.

혼란스러운 부분, 참조 유형을 확인하십시오.

참조 유형을 만들 수 있습니다.

List<string> someobject = new List<string>()

someobject 를 새로 만들면 두 부분이 생성됩니다.

  1. someobject에 대한 데이터를 보유하는 메모리 블록입니다 .
  2. 해당 데이터 블록에 대한 참조 (포인터).

이제 당신이 ref없이 메소드 에 어떤 객체 를 보낼 때 데이터가 아닌 참조 포인터를 복사합니다 . 이제 당신은 이것을 가지고 있습니다 :

(outside method) reference1 => someobject
(inside method)  reference2 => someobject

동일한 객체를 가리키는 두 개의 참조 reference2를 사용하여 일부 객체 의 속성을 수정하면 reference1이 가리키는 동일한 데이터에 영향을 미칩니다.

 (inside method)  reference2.Add("SomeString");
 (outside method) reference1[0] == "SomeString"   //this is true

reference2를 널 아웃하거나 새 데이터를 가리키면 reference1 또는 data1이 가리키는 데이터에 영향을 미치지 않습니다.

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

이제 객체 에 대한 참조로 메소드 를 보내면 어떻게됩니까 ? 실제 기준SomeObject의이 방법에 보내지. 따라서 이제 데이터에 대한 참조가 하나만 있습니다.

(outside method) reference1 => someobject;
(inside method)  reference1 => someobject;

그러나 이것은 무엇을 의미합니까? 그것은 두 가지 주요한 것을 제외하고 ref가 아닌 객체를 보내는 것과 정확히 동일하게 작동합니다.

1) 메소드 내부의 참조를 무효화하면 메소드 외부의 참조를 무효화합니다.

 (inside method)  reference1 = null;
 (outside method) reference1 == null;  //true

2) 이제 완전히 다른 데이터 위치를 가리키는 참조를 지정할 수 있으며 함수 외부의 참조는 이제 새 데이터 위치를 가리 킵니다.

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true

결국 (참조의 경우) 데이터에 대한 하나의 참조 만 있고 두 개의 별칭이 있음을 의미합니다. 권리?
Sadiq

3
명확한 설명을 위해 찬성했습니다. 그러나 나는 그 차이를 설명하지 않는 한이 질문에 대답하지 않는 생각 refout매개 변수를.
Joyce Babu

1
놀랄 만한. out키워드 와 동일하게 설명 할 수 있습니까?
Asif Mushtaq

28

심판에 아웃 .

out요구 사항에 적합한 곳이면 어디에서나 사용 하십시오.


받아 들여지지 않은 대답은 방향이없고 쓸모없는 값 유형을 무시하지 않으면 무시합니다.
kenny

@ kenny : 약간의 설명을 해줄 수 있습니까-예를 들어, 대답의 정신을 유지하면서 인식하는 부정확성을 제거하기 위해 어떤 단어를 변경 하시겠습니까? 내 대답은 초보자의 미친 추측은 아니지만 귀하의 의견에 서두름 (단순, 오타)이 있다고 가정하는 것 같습니다. 목표는 단어 수가 가장 적은 차이에 대해 생각할 수있는 방법을 제공하는 것입니다.
Ruben Bartelink

(BTW 나는 값 유형, 참조 유형, 참조로 전달, 값으로 전달, COM 및 C ++에 익숙합니다. 설명에서 이러한 개념을 참조하는 것이 유용하다고 생각되면)
Ruben Bartelink

1
객체 참조는 값으로 전달됩니다 ( "ref"또는 "out"키워드를 사용하는 경우 제외). 객체를 ID 번호로 생각하십시오. 클래스 변수에 "Object # 1943"이 있고 하나의 값으로 루틴에 변수를 전달하는 경우 해당 루틴은 Object # 1943을 변경할 수 있지만 변수가 "Object # 1943"이외의 항목을 가리 키도록 할 수는 없습니다. 변수가 참조로 전달 된 경우 루틴은 변수 점을 "개체 # 5441"로 유지할 수 있습니다.
supercat

1
@ supercat : ref vs val (및이 후속 분석)에 대한 설명이 마음에 듭니다. 나는 케니가 실제로 그에게 설명 된 어떤 것도 필요하지 않다고 생각한다. 나는 모든 사람들을 혼란스럽게하지만이 goddam 주석을 모두 제거 할 수 있기를 바랍니다. 이 모든 넌센스의 근본 원인은 케니가 내 대답을 잘못 읽었으며 추가 / 제거 / 대체해야 할 한 단어를 지적하지 않은 것으로 보입니다. 우리 중 세 사람은 우리가 이미 알지 못한 토론에서 아무것도 배우지 않았으며 다른 대답에는 터무니없는 찬성 수가 있습니다.
Ruben Bartelink

18

밖:

C #에서 메서드는 하나의 값만 반환 할 수 있습니다. 둘 이상의 값을 반환하려면 out 키워드를 사용할 수 있습니다. 출력 수정자는 참조 기준으로 반환됩니다. 가장 간단한 대답은 키워드 "out"을 사용하여 메소드에서 값을 얻는 것입니다.

  1. 호출 함수에서 값을 초기화 할 필요는 없습니다.
  2. 호출 된 함수에 값을 할당해야합니다. 그렇지 않으면 컴파일러에서 오류를보고합니다.

심판 :

C #에서는 int, float, double 등과 같은 값 형식을 메서드 매개 변수의 인수로 전달하면 값으로 전달됩니다. 따라서 매개 변수 값을 수정하면 메소드 호출의 인수에 영향을 미치지 않습니다. 그러나“ref”키워드로 매개 변수를 표시하면 실제 변수에 반영됩니다.

  1. 함수를 호출하기 전에 변수를 초기화해야합니다.
  2. 메소드의 ref 매개 변수에 값을 지정하지 않아도됩니다. 값을 변경하지 않으면 "ref"로 표시해야합니까?

"C #에서 메소드는 하나의 값만 리턴 할 수 있습니다. 둘 이상의 값을 리턴하려면 out 키워드를 사용할 수 있습니다." 값을 반환하기 위해 "ref"를 사용할 수도 있습니다. 메소드에서 여러 값을 반환하려면 ref와 out을 모두 사용할 수 있습니까?
Ned

1
c # 7에서는 ValueTuples를 사용하여 여러 값을 반환 할 수 있습니다.
Iman Bahrampour

13

개 확장, 고양이 예. ref가있는 두 번째 메소드는 호출자가 참조하는 오브젝트를 변경합니다. 따라서 "고양이"!!!

    public static void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog". 
        Bar(ref myObject);
        Console.WriteLine(myObject.Name); // Writes "Cat". 
    }

    public static void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

    public static void Bar(ref MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

8

참조 유형 (클래스)을 전달하기 ref때문에 기본적으로 실제 객체에 대한 참조 만 전달되므로 항상 참조 뒤의 객체를 변경 하기 때문에 사용할 필요가 없습니다 .

예:

public void Foo()
{
    MyClass myObject = new MyClass();
    myObject.Name = "Dog";
    Bar(myObject);
    Console.WriteLine(myObject.Name); // Writes "Cat".
}

public void Bar(MyClass someObject)
{
    someObject.Name = "Cat";
}

클래스를 전달하는 한 ref메소드 내에서 객체를 변경하려는 경우 사용할 필요가 없습니다 .


5
새 객체를 만들어서 반환하지 않은 경우에만 작동합니다. 새 개체를 만들면 이전 개체에 대한 참조가 손실됩니다.
etsuba

8
이것은 잘못된 것입니다 - 다음을 시도해보십시오 추가 someObject = null하는 Bar실행 종료. Bar인스턴스에 대한 참조 만 null 이므로 코드가 제대로 실행됩니다 . 이제 변경 BarBar(ref MyClass someObject)다시 실행 - 당신이를 얻을 수 있습니다 NullReferenceException때문에 Foo인스턴스에의 참조가 너무으로 널되었습니다.
Keith

8

ref그리고 out다음과 같은 차이점을 제외하고 유사하게 동작합니다.

  • ref변수는 사용하기 전에 초기화해야합니다. out할당없이 변수를 사용할 수 있습니다
  • out매개 변수는이를 사용하는 함수에 의해 할당되지 않은 값으로 처리되어야합니다. 따라서 out호출 코드에서 초기화 된 매개 변수를 사용할 수 있지만 함수가 실행될 때 값이 손실됩니다.


6

"빵 굽는 사람"

첫 번째는 문자열 참조가 "Baker"를 가리 키도록 변경하기 때문입니다. ref 키워드를 통해 참조를 전달했기 때문에 참조를 변경할 수 있습니다 (=> 문자열에 대한 참조). 두 번째 호출은 문자열에 대한 참조 사본을 가져옵니다.

문자열은 처음에는 특별한 것으로 보입니다. 그러나 문자열은 단지 참조 클래스이며 정의하면

string s = "Able";

s는 텍스트 "Able"을 포함하는 문자열 클래스에 대한 참조입니다! 같은 변수에 대한 또 다른 할당

s = "Baker";

원래 문자열을 변경하지 않고 새 인스턴스를 만들고 해당 인스턴스를 가리켜 봅시다!

다음과 같은 작은 코드 예제로 시도해 볼 수 있습니다.

string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);

당신은 무엇을 기대합니까? s2가 원래 인스턴스를 가리키는 동안 s의 참조를 다른 인스턴스로 설정하기 때문에 여전히 "Able"이됩니다.

편집 : 문자열도 변경할 수 없으므로 기존 문자열 인스턴스를 수정하는 메서드 또는 속성이 없습니다 (문서에서 찾을 수는 있지만 :-)는 찾지 않습니다). 모든 문자열 조작 방법은 새로운 문자열 인스턴스를 반환합니다! (그래서 StringBuilder 클래스를 사용할 때 종종 더 나은 성능을 얻는 이유)


1
바로 그거죠. 따라서 "참조 유형 (클래스)을 전달하기 때문에 사용 참조가 필요하지 않습니다"라고 말하는 것은 사실이 아닙니다.
Paul Mitchell

이론적으로 그는 문자열에서 불가능한 "수정 될 수 있도록"썼기 때문에 그렇게 말하는 것이 옳다. 그러나 불변의 객체 때문에 "ref"와 "out"은 참조 유형에도 매우 유용합니다! (.Net에는 많은 불변의 클래스가 포함되어 있습니다!)
mmmmmmmm

네 말이 맞아. 대부분의 객체가 변경 가능하기 때문에 문자열과 같은 불변 객체를 생각하지 않았습니다.
Albic

1
글쎄, 이것은 LQP에서 볼 수있는 수수께끼의 대답입니다. 포럼이었던 것처럼 다른 의견에 대한 길고 철저한 응답으로 보이는 것 외에는 문제가 없습니다. 나는 그것이 실제로 분류되지 않았을 때 생각합니다.
Nathan Tuggy

6

ref 는 ref 매개 변수의 값이 이미 설정되어 있으며 메소드가이를 읽고 수정할 수 있음을 의미합니다. ref 키워드를 사용하는 것은 호출자가 매개 변수의 값을 초기화 할 책임이 있다고 말하는 것과 같습니다.


out 은 객체의 초기화가 함수의 책임이며 컴파일러가 함수에 out 매개 변수를 할당해야 함을 컴파일러에 알려줍니다. 할당되지 않은 채로 둘 수 없습니다.


5

Out : 함수에서 하나의 값만 반환하기 위해 return 문을 사용할 수 있습니다. 그러나 출력 매개 변수를 사용하면 함수에서 두 개의 값을 리턴 할 수 있습니다. 출력 매개 변수는 데이터를 메소드가 아닌 메소드 외부로 전송한다는 점을 제외하고는 참조 매개 변수와 같습니다.

다음 예제는이를 보여줍니다.

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void getValue(out int x )
      {
         int temp = 5;
         x = temp;
      }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;

         Console.WriteLine("Before method call, value of a : {0}", a);

         /* calling a function to get the value */
         n.getValue(out a);

         Console.WriteLine("After method call, value of a : {0}", a);
         Console.ReadLine();

      }
   }
}

ref : 참조 매개 변수는 변수의 메모리 위치에 대한 참조입니다. 값 매개 변수와 달리 참조로 매개 변수를 전달하면이 매개 변수에 대해 새 스토리지 위치가 작성되지 않습니다. 참조 매개 변수는 메소드에 제공되는 실제 매개 변수와 동일한 메모리 위치를 나타냅니다.

C #에서는 ref 키워드를 사용하여 참조 매개 변수를 선언합니다. 다음 예제는이를 보여줍니다.

using System;
namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* save the value of x */
         x = y;   /* put y into x */
         y = temp; /* put temp into y */
       }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;
         int b = 200;

         Console.WriteLine("Before swap, value of a : {0}", a);
         Console.WriteLine("Before swap, value of b : {0}", b);

         /* calling a function to swap the values */
         n.swap(ref a, ref b);

         Console.WriteLine("After swap, value of a : {0}", a);
         Console.WriteLine("After swap, value of b : {0}", b);

         Console.ReadLine();

      }
   }
}

4

ref 및 out은 C ++에서와 같이 참조를 전달하고 포인터를 전달하는 것처럼 작동합니다.

심판의 경우, 인수를 선언하고 초기화해야합니다.

밖으로, 인수는 선언해야하지만 초기화되거나 초기화되지 않을 수 있습니다

        double nbr = 6; // if not initialized we get error
        double dd = doit.square(ref nbr);

        double Half_nbr ; // fine as passed by out, but inside the calling  method you initialize it
        doit.math_routines(nbr, out Half_nbr);

1
변수를 인라인으로 선언 할 수 있습니다 out double Half_nbr..
Sebastian Hofmann

4

제작 시간 :

(1) 우리는 호출 방법을 만듭니다 Main()

(2) List 객체 (참조 형 객체)를 생성하여 변수에 저장합니다 myList.

public sealed class Program 
{
    public static Main() 
    {
        List<int> myList = new List<int>();

런타임 중 :

(3) 런타임은 주소를 저장하기에 충분히 넓은 # 00에서 스택에 메모리를 할당합니다 ( myList변수 이름은 실제로 메모리 위치의 별명이기 때문에 # 00 = )

(4) 런타임은 메모리 위치 #FF에서 힙에 목록 객체를 생성합니다 (이 모든 주소는 예를 들어 술입니다)

(5) 런타임은 # 00에 객체의 시작 주소 #FF를 저장합니다 (즉, List 객체의 참조를 포인터에 저장합니다 myList)

제작 시간으로 돌아 가기 :

(6) 그런 다음 List 객체를 myParamList호출 된 메서드에 인수로 전달 modifyMyList하고 새 List 객체를 할당합니다.

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

런타임 중 :

(7) 런타임은 호출 된 메소드의 호출 루틴을 시작하고 그 일부로 매개 변수 유형을 확인합니다.

(8) 참조 유형을 찾으면 매개 변수 variable의 별칭을 지정하기 위해 # 04에서 스택에 메모리를 할당합니다 myParamList.

(9) 그런 다음 #FF 값도 저장합니다.

(10) 런타임은 메모리 위치 # 004에서 힙에 목록 객체를 만들고 # 04의 #FF를이 값으로 바꿉니다 (또는 원래 List 객체를 역 참조하고이 방법에서 새 List 객체를 가리킴)

# 00의 주소는 변경되지 않으며 #FF에 대한 참조를 유지합니다 (또는 원래 myList포인터가 방해받지 않음).


심판 키워드는 메소드 파라미터위한 힙 할당 없을 것이다 수단 (8) 및 (9)에 대한 실행 코드의 생성을 스킵하도록 컴파일러에 지시한다. 원래 # 00 포인터를 사용하여 #FF에서 개체를 조작합니다. 원래 포인터가 초기화되지 않으면 런타임이 변수가 초기화되지 않아 진행할 수 없다는 불평을 중단합니다.

아웃 키워드는 꽤 많이 (9)와 (10)에 약간의 수정과 심판과 동일한 컴파일러 지시문입니다. 컴파일러는 인수가 초기화되지 않을 것으로 예상하고 (8), (4) 및 (5)로 계속해서 힙에 객체를 만들고 시작 변수를 인수 변수에 저장합니다. 초기화되지 않은 오류는 발생하지 않으며 저장된 이전 참조는 손실됩니다.


3

또한 다른 사람의 변수를 클래스의 다른 인스턴스에 재 할당하고 여러 값 등을 반환 하거나 다른 사람이 필요한 것을 사용 ref하거나 out다른 사람에게 제공하는 변수로 무엇을 할 것인지 알려줍니다.

  • 당신 은 필요하지 않습니다 ref 또는 out당신이 할거야 모두 수정 일 경우 내부MyClass 인수에 전달되는 경우 someClass.

    • 호출하는 방법과 같은 변화가 나타납니다 someClass.Message = "Hello World"당신이 사용하지 여부 ref, out또는 아무것도
    • someClass = new MyClass()내부 쓰기myFunction(someClass) 메소드 someClass의 범위 myFunction에서만 보이는 객체가 교체 됩니다. 호출 메소드는 여전히 MyClass작성하여 메소드에 전달한 원래 인스턴스 에 대해 알고 있습니다.
  • 당신 필요 ref 하거나 out당신이 교환에 계획하는 경우에 someClass호출하는 방법을 완전히 새로운 개체에 대한 아웃하고 원하는 변경 사항을 확인하려면

    • 쓰기 someClass = new MyClass()내부를myFunction(out someClass) 호출 한 메소드에 의해 표시된 객체가 변경됩니다myFunction

다른 프로그래머들이 존재합니다

그리고 그들은 당신이 그들의 데이터로 무엇을할지 알고 싶어합니다. 수백만 명의 개발자가 사용할 라이브러리를 작성한다고 가정하십시오. 그들이 메소드를 호출 할 때 변수로 무엇을할지 알고 싶어합니다.

  • 사용 ref 하면 "메서드를 호출 할 때 어떤 값에 할당 된 변수를 전달합니다. 내 메소드를 수행하는 동안 다른 것으로 변경 될 수 있다는 점에 유의하십시오. 변수가 이전 오브젝트를 가리키는 것으로 기대하지 마십시오. 내가 끝나면 "

  • 를 사용 out하면 "내 메소드에 자리 표시 자 변수를 전달합니다. 값이 있는지 여부는 중요하지 않습니다. 컴파일러에서 강제로 새 값을 지정하도록합니다. 내 메소드를 호출하기 전에 변수, 내가 다 했어 시간에 따라 다를 수

그런데 C # 7.2에는 in 수정 자도 있습니다.

그리고 메소드가 전달 된 인스턴스를 다른 인스턴스로 교체하지 못하게합니다. 수백만 명의 개발자에게 "원본 변수 참조를 전달하면 신중하게 제작 된 데이터를 다른 것으로 바꾸지 않겠다고 약속합니다"라고 생각하십시오. in일부 특이 사항이 있으며 일부 경우에 귀하의 단편과 호환되도록 암시 적 변환이 필요할 수 있습니다.in int 컴파일러 일시적으로 int를 만들고 짧게 확장하여 참조로 전달하고 마무리합니다. 당신이 그것을 망치지 않을 것이라고 선언했기 때문에이 작업을 수행 할 수 있습니다.


Microsoft는 .TryParse숫자 유형에 대한 방법으로이를 수행했습니다.

int i = 98234957;
bool success = int.TryParse("123", out i);

out여기에 적극적으로 선언 할 때 매개 변수에 플래그를 지정하여 " 분명히 다른 것을 위해 98234957의 당신의 노력으로 만들어진 가치를 바꿀 것입니다"

물론, 파싱 방법이 다른 것의 값 유형을 바꾸는 것이 허용되지 않는다면 가치 유형을 구문 분석하는 것과 같은 것들에 대해서는 다소 필요합니다. 만들고있는 라이브러리 :

public void PoorlyNamedMethod(out SomeClass x)

당신은 그것이 인 out것을 알 수 있습니다. 따라서 숫자를 처리하는 데 몇 시간을 보내면 완벽한 SomeClass를 만듭니다.

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

글쎄요, 그 시간을 낭비해서 완벽한 수업을하는데 확실히 던져지고 PoorlyNamedMethod로 대체 될 것입니다.


3

간결한 답변을 찾는 사람들에게.

refout키워드는 모두 통과하는 데 사용됩니다 reference.


ref키워드 변수 에는 값이 있거나 객체를 참조하거나 전달 null 하기 전에 참조해야합니다 .


와 달리 키워드 ref의 변수 out는 값을 가져야하거나 객체를 참조하거나 전달한 null 후에 전달해야하며 값을 가지거나 객체 참조 할 필요가 없습니다 .


2

많은 훌륭한 설명을 설명하기 위해 다음 콘솔 앱을 개발했습니다.

using System;
using System.Collections.Generic;

namespace CSharpDemos
{
  class Program
  {
    static void Main(string[] args)
    {
      List<string> StringList = new List<string> { "Hello" };
      List<string> StringListRef = new List<string> { "Hallo" };

      AppendWorld(StringList);
      Console.WriteLine(StringList[0] + StringList[1]);

      HalloWelt(ref StringListRef);
      Console.WriteLine(StringListRef[0] + StringListRef[1]);

      CiaoMondo(out List<string> StringListOut);
      Console.WriteLine(StringListOut[0] + StringListOut[1]);
    }

    static void AppendWorld(List<string> LiStri)
    {
      LiStri.Add(" World!");
      LiStri = new List<string> { "¡Hola", " Mundo!" };
      Console.WriteLine(LiStri[0] + LiStri[1]);
    }

    static void HalloWelt(ref List<string> LiStriRef)
     { LiStriRef = new List<string> { LiStriRef[0], " Welt!" }; }

    static void CiaoMondo(out List<string> LiStriOut)
     { LiStriOut = new List<string> { "Ciao", " Mondo!" }; }
   }
}
/*Output:
¡Hola Mundo!
Hello World!
Hallo Welt!
Ciao Mondo!
*/
  • AppendWorld: StringList명명 된 사본 LiStri이 전달됩니다. 메소드 시작시이 사본은 원래 목록을 참조하므로이 목록을 수정하는 데 사용할 수 있습니다. 나중에 메서드 내에서 원래 목록에 영향을 미치지 않는 LiStri다른 List<string>개체를 참조 합니다.

  • HalloWelt: LiStriRef이미 초기화 된 별칭입니다 ListStringRef. 전달 된 List<string>객체는 새로운 객체를 초기화하는 데 사용되므로 ref필요했습니다.

  • CiaoMondo: LiStriOut의 별칭이며 ListStringOut초기화해야합니다.

따라서 메소드가 전달 된 변수가 참조하는 객체를 수정하는 경우 컴파일러에서 사용할 outref없으며 컴파일러가 아니라 코드 판독기를 혼동 하기 때문에 사용해서는 안됩니다 . 메소드가 전달 된 인수를 다른 오브젝트를 참조하게하려면 ref이미 초기화 된 오브젝트 및 out전달 된 인수의 새 오브젝트를 초기화해야하는 메소드에 사용하십시오. 게다가, refout동일하게 동작.


1

그것들은 거의 동일합니다-유일한 차이점은 out 매개 변수로 전달하는 변수를 초기화 할 필요가 없으며 ref 매개 변수를 사용하는 방법으로 변수를 설정해야한다는 것입니다.

int x;    Foo(out x); // OK 
int y;    Foo(ref y); // Error

Ref 매개 변수는 수정 될 수있는 데이터를위한 것이고 out 매개 변수는 이미 무언가에 대한 반환 값을 사용하는 함수 (예 : int.TryParse)에 대한 추가 출력 인 데이터를위한 것입니다.


1

아래에서는 Refout 을 모두 사용하는 예를 보여 주었습니다 . 이제, 당신은 심판과 외출에 대해 지워질 것입니다.

아래에 언급 된 예제에서 // myRefObj = new myClass {Name = "ref outside called !!"}; 줄에 "할당되지 않은 로컬 변수 'myRefObj'사용"이라는 오류가 표시되지만 out에 해당 오류가 없습니다 .

Ref 사용 위치 : in 매개 변수 를 사용 하여 프로 시저를 호출 할 때 동일한 매개 변수가 해당 proc의 출력을 저장하는 데 사용됩니다.

사용 위치 : in 매개 변수없이 프로 시저를 호출 할 때 동일한 매개 변수가 해당 proc에서 값을 리턴하는 데 사용됩니다. 출력에 주목

public partial class refAndOutUse : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        myClass myRefObj;
        myRefObj = new myClass { Name = "ref outside called!!  <br/>" };
        myRefFunction(ref myRefObj);
        Response.Write(myRefObj.Name); //ref inside function

        myClass myOutObj;
        myOutFunction(out myOutObj);
        Response.Write(myOutObj.Name); //out inside function
    }

    void myRefFunction(ref myClass refObj)
    {
        refObj.Name = "ref inside function <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 

1
 public static void Main(string[] args)
    {
        //int a=10;
        //change(ref a);
        //Console.WriteLine(a);
        // Console.Read();

        int b;
        change2(out b);
        Console.WriteLine(b);
        Console.Read();
    }
    // static void change(ref int a)
    //{
    //    a = 20;
    //}

     static void change2(out int b)
     {
         b = 20;
     }

이 코드를 확인하면 "ref"를 사용할 때의 완전한 차이점을 설명 할 수 있습니다. 그 의미는 이미 int / string을 초기화한다는 의미입니다.

그러나 "out"을 사용할 때 두 조건 모두에서 작동합니다. 위의 u는 int / string을 초기화합니다. 그러나 u는 해당 함수에서 int / string을 초기화해야합니다.


1

Ref : ref 키워드는 인수를 참조로 전달하는 데 사용됩니다. 이는 메소드에서 해당 매개 변수의 값이 변경되면 호출 메소드에 반영됨을 의미합니다. ref 키워드를 사용하여 전달 된 인수는 호출 된 메소드로 전달되기 전에 호출 메소드에서 초기화되어야합니다.

Out : out 키워드는 ref 키워드와 같은 인수를 전달하는 데에도 사용되지만 값을 지정하지 않고 인수를 전달할 수 있습니다. out 키워드를 사용하여 전달 된 인수는 호출 된 메소드로 돌아 가기 전에 호출 된 메소드에서 초기화되어야합니다.

public class Example
{
 public static void Main() 
 {
 int val1 = 0; //must be initialized 
 int val2; //optional

 Example1(ref val1);
 Console.WriteLine(val1); 

 Example2(out val2);
 Console.WriteLine(val2); 
 }

 static void Example1(ref int value) 
 {
 value = 1;
 }
 static void Example2(out int value) 
 {
 value = 2; 
 }
}

/* Output     1     2     

메서드 오버로딩에서 참조 및 출력

ref와 out은 동시에 메소드 오버로드에 사용될 수 없습니다. 그러나 ref와 out은 런타임에 다르게 처리되지만 컴파일 타임에 동일하게 처리됩니다 (ref 및 out에 대한 IL을 생성하는 동안 CLR은 둘을 구분하지 않습니다).


0

파라미터의 차이를 수신하는 방법의 관점에서 ref그리고 out그 C #을하는 방법은 매에 기록해야 필요 out돌아 가기 전에 변수, 및로 전달하는 대신, 이러한 매개 변수와 다른 아무것도하지해야 out파라미터 또는 서면 out다른 메소드에 매개 변수로 전달 되거나 직접 작성 될 때까지 . 일부 다른 언어는 그러한 요구 사항을 강요하지 않습니다. C #에서 out매개 변수로 선언 된 가상 또는 인터페이스 메서드 는 다른 매개 변수로 재정의 될 수 있으며 이러한 매개 변수에 대한 특별한 제한은 없습니다.

호출자의 관점에서 C #은 out매개 변수를 사용 하여 메서드를 호출 할 때 전달 된 변수가 먼저 읽히지 않고 쓰여질 때 많은 상황을 가정 합니다. 다른 언어로 작성된 메소드를 호출 할 때이 가정이 올바르지 않을 수 있습니다. 예를 들면 다음과 같습니다.

struct MyStruct
{
   ...
   myStruct(IDictionary<int, MyStruct> d)
   {
     d.TryGetValue(23, out this);
   }
}

C # 이외의 언어로 작성된 구현을 myDictionary식별하는 경우 과제처럼 보이지만 잠재적으로 떠날 수 있습니다.IDictionary<TKey,TValue>MyStruct s = new MyStruct(myDictionary);s 수정되지 않은.

VB.NET으로 작성된 생성자는 C #의 생성자와 달리 호출 된 메소드가 out매개 변수 를 수정하는지 여부에 대한 가정을하지 않으며 모든 필드를 무조건 지 웁니다. 위에서 언급 한 이상한 동작은 VB 또는 C #으로 작성된 코드에서는 발생하지 않지만 C #으로 작성된 코드가 VB.NET으로 작성된 메서드를 호출 할 때 발생할 수 있습니다.


0

매개 변수를 참조로 전달하려면 매개 변수를 함수에 전달하기 전에 매개 변수를 초기화해야합니다. 그렇지 않으면 컴파일러 자체가 오류를 표시합니다. 그러나 매개 변수가 없으면 매개 변수를 전달하기 전에 개체 매개 변수를 초기화 할 필요가 없습니다. 호출 메소드 자체에서 오브젝트를 초기화 할 수 있습니다.


-3

함수 내부에 전달 된 참조 매개 변수가 직접 처리된다는 점에 유의하십시오.

예를 들어

    public class MyClass
    {
        public string Name { get; set; }
    }

    public void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog".
    }

    public void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

이것은 고양이가 아니라 개를 쓸 것입니다. 따라서 someObject에서 직접 작업해야합니다.


6
여기의 모든 것이 사실이지만, 기준에 의한 가치 나 가치의 차이를 실제로 설명하지는 않습니다. 기껏해야 절반은 참조 및 값 / 불변 유형의 차이점을 설명합니다.
Conrad Frix

해당 코드를 cat로 작성하려면 다음과 같이 'ref'키와 함께 해당 객체를 전달하십시오. public static void Bar (ref MyClass someObject), Bar (ref myObject);
다니엘 보테 코레아

-4

나는 이것에 익숙하지 않을 수도 있지만 분명히 문자열은 (기술적으로 참조 유형이고 힙에 살고 있지만) 참조가 아닌 값으로 전달됩니까?

        string a = "Hello";

        string b = "goodbye";

        b = a; //attempt to make b point to a, won't work.

        a = "testing";

        Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!

그렇기 때문에 변경하는 함수의 범위를 벗어나는 변경 사항을 원한다면 참조가 필요합니다. 그렇지 않으면 참조를 전달하지 않습니다.

내가 아는 한 구조체 / 값 유형과 문자열 자체에 대한 참조 만 필요합니다. 문자열은 척하는 참조 유형이지만 값 유형은 아닙니다.

나는 여기서 완전히 틀릴 수 있습니다.


5
에드윈, 스택 오버플로에 오신 것을 환영합니다. 내가 아는 한 다른 객체와 마찬가지로 문자열은 참조로 전달됩니다. 문자열은 불변의 객체이기 때문에 혼란 스러울 수 있으므로 참조로 전달되는 것은 분명하지 않습니다. 문자열에 문자열 Capitalize()의 내용을 대문자로 변경 하는 메소드가 있다고 상상해보십시오 . 당신이 다음 라인을 교체 한 경우 a = "testing";a.Capitalize();, 다음 출력은 "HELLO"가 아닌 "안녕하세요"가 될 것입니다. 불변 유형의 장점 중 하나는 참조를 전달하고 다른 코드가 값을 변경하는 것에 대해 걱정할 수 없다는 것입니다.
돈 커크비

2
유형이 노출 할 수있는 세 가지 기본 유형의 시맨틱이 있습니다 : 가변 참조 시맨틱, 가변 값 시맨틱 및 불변 시맨틱. 필드 또는 속성 m을 갖는 T 유형의 변수 x 및 y를 고려하고 x가 y에 복사되었다고 가정하십시오. T에 참조 시맨틱이있는 경우 xm으로의 변경은 ym에 의해 관찰됩니다. T에 값 시맨틱이 있으면 ym에 영향을주지 않고 xm을 변경할 수 있습니다. 불변 시맨틱은 참조 또는 값 객체로 시뮬레이션 할 수 있습니다. 문자열은 변경할 수없는 참조 객체입니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.