Program Tip

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

programtip 2020. 9. 28. 09:59
반응형

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


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

public void myFunction(ref MyClass someClass)

public void myFunction(out MyClass someClass)

어떤 것을 사용해야하며 그 이유는 무엇입니까?


ref함수에 들어가기 전에 개체가 초기화 out되었음을 컴파일러에 알리고 함수 내에서 개체가 초기화 될 것임을 컴파일러에 알립니다.

그래서 양방향 refout아웃 전용입니다.


ref수정 수단 :

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

out수정 수단 :

  1. 값이 설정되지 않았으며 설정 될 때까지 메서드에서 읽을 수 없습니다 .
  2. 반환하기 전에 메서드 설정 해야합니다 .

Dom이 TPS 보고서에 대한 메모에 대해 Peter의 큐비클에 나타난다 고 가정 해 보겠습니다.

Dom이 심판 주장이라면 메모의 인쇄본을 갖게 될 것입니다.

Dom이 논쟁을 벌였다면 Peter에게 메모를 새로 인쇄하여 가져갈 수 있도록했습니다.


나는 설명에 내 손을 시험 할 것이다.

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

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

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

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

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

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

이제 ref없이 someobject 를 메서드에 보낼 때 데이터가 아닌 참조 포인터를 복사합니다 . 이제 다음이 있습니다.

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

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

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

reference2를 null로 설정하거나 새 데이터를 가리키면 reference1이나 reference1이 가리키는 데이터에 영향을주지 않습니다.

(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

이제 메서드에 ref로 someobject 를 보내면 어떻게 될까요? someobject에 대한 실제 참조메서드로 전송됩니다. 따라서 이제 데이터에 대한 참조가 하나만 있습니다.

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

그러나 이것은 무엇을 의미합니까? 두 가지 주요 사항을 제외하고 ref가 아닌 someobject를 보내는 것과 똑같이 작동합니다.

1) 메서드 내부의 참조를 null로 만들면 메서드 외부의 참조가 null이됩니다.

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

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

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

심판은 안으로 밖으로 입니다.

귀하 out의 요구 사항에 적합한 곳에서 선호 하여 사용해야 합니다.


밖:

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

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

심판 :

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

  1. 함수를 호출하기 전에 변수를 초기화해야합니다.
  2. 메서드의 ref 매개 변수에 값을 할당 할 필요는 없습니다. 값을 변경하지 않는 경우 "ref"로 표시해야하는 이유는 무엇입니까?

Dog, Cat 예제 확장. 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;
    }

참조 유형 (클래스)을 전달하기 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메서드 내에서 객체를 변경하려는 경우 사용할 필요가 없습니다 .


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

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

예를 들어 (나와 같은) 배우는 사람들을 위해 Anthony Kolesov가 말하는 내용 은 다음과 같습니다 .

요점을 설명하기 위해 ref, out 및 기타의 최소한의 예제를 만들었습니다. 모범 사례를 다루는 것이 아니라 차이점을 이해하기위한 예일뿐입니다.

https://gist.github.com/2upmedia/6d98a57b68d849ee7091


"빵 굽는 사람"

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

문자열은 처음에는 특별한 것처럼 보입니다. 하지만 문자열은 참조 클래스 일 뿐이며

string s = "Able";

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

s = "Baker";

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

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

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

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

편집 : 문자열은 또한 변경 불가능합니다. 즉, 기존 문자열 인스턴스를 수정하는 메서드 나 속성이 없음을 의미합니다 (문서에서 찾을 수는 있지만 찾을 수는 없지만 :-)). 모든 문자열 조작 메서드는 새 문자열 인스턴스를 반환합니다! (이것이 StringBuilder 클래스를 사용할 때 종종 더 나은 성능을 얻는 이유입니다)


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


out 은 객체 초기화가 함수의 책임이며 함수가 out 매개 변수에 할당해야 함을 컴파일러에 알립니다. 할당되지 않은 상태로 두는 것은 허용되지 않습니다.


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

      }
   }
}

ref 및 out은 C ++에서와 같이 참조로 전달하고 포인터로 전달하는 것과 같습니다.

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

out의 경우 인수를 선언해야하지만 초기화되거나 초기화되지 않을 수 있습니다.

        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) 호출 방법을 만듭니다. Main()

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

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

런타임 중 :

(3) 런타임은 # 00에서 스택에 메모리를 할당합니다. 주소를 저장할 수있을만큼 넓습니다 (# 00 = myList, 변수 이름은 실제로 메모리 위치의 별칭 일 뿐이므로)

(4) 런타임은 메모리 위치 #FF의 힙에 목록 개체를 만듭니다 (이 주소는 모두 예를 들어).

(5) 런타임은 객체의 시작 주소 #FF를 # 00에 저장합니다 (또는 단어로 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) 참조 유형을 찾으면 매개 변수 변수의 별칭을 지정하기 위해 # 04의 스택에 메모리를 할당합니다 myParamList.

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

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

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


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

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


거의 동일합니다. 유일한 차이점은 out 매개 변수로 전달하는 변수를 초기화 할 필요가없고 ref 매개 변수를 사용하는 메소드가이를 무언가로 설정해야한다는 것입니다.

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

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


아래는 Refout 을 모두 사용하는 예제를 보여줍니다 . 이제, 여러분 모두 ref와 out에 대해 지워질 것입니다.

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

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

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

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

 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"를 사용할 때 완전한 차이점을 설명 할 수 있습니다. 이는 u가 이미 해당 정수 / 문자열을 초기화한다는 의미입니다.

그러나 "out"을 사용하면 두 조건 모두에서 작동합니다. 두 조건 모두에서 해당 int / string을 초기화하거나 초기화하지 않지만 u는 해당 함수에서 해당 int / string을 초기화해야합니다.


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     

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

Both ref and out cannot be used in method overloading simultaneously. However, ref and out are treated differently at run-time but they are treated same at compile time (CLR doesn't differentiates between the two while it created IL for ref and out).


As well as allowing you to reassign someone else's variable to a different instance of a class, return multiple values etc, using ref or out lets someone else know what you need from them and what you intend to do with the variable they provide

  • You don't need ref or out if all you're going to do is modify things inside the MyClass instance that is passed in the argument someClass.

    • The calling method will see changes like someClass.Message = "Hello World" whether you use ref, out or nothing
    • Writing someClass = new MyClass() inside myFunction(someClass) swaps out the object seen by the someClass in the scope of the myFunction method only. The calling method still knows about the original MyClass instance it created and passed to your method
  • You need ref or out if you plan on swapping the someClass out for a whole new object and want the calling method to see your change

    • Writing someClass = new MyClass() inside myFunction(out someClass) changes the object seen by the method that called myFunction

Other programmers exist

And they want to know what you're going to do with their data. Imagine you're writing a library that will be used by millions of developers. You want them to know what you're going to do with their variables when they call your methods

  • Using ref makes a statement of "Pass a variable assigned to some value when you call my method. Be aware that I might change it out for something else entirely during the course of my method. Do not expect your variable to be pointing to the old object when I'm done"

  • Using out makes a statement of "Pass a placeholder variable to my method. It doesn't matter whether it has a value or not; the compiler will force me to assign it to a new value. I absolutely guarantee that the object pointed to by your variable before you called my method, will be different by the time I'm done

By the way, in C#7.2 there's an in modifier too

And that prevents the method from swapping out the passed in instance for a different instance. Think of it like saying to those millions of developers "pass me your original variable reference, and I promise not to swap your carefully crafted data out for something else". in has some peculiarities, and in some cases such as where an implicit conversion might be required to make your short compatible with an in int the compiler will temporarily make an int, widen your short to it, pass it by reference and finish up. It can do this because you've declared you're not going to mess with it.


Microsoft did this with the .TryParse methods on the numeric types:

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

By flagging the parameter as out they're actively declaring here "we are definitely going to change your painstakingly crafted value of 98234957 out for something else"

Of course, they kinda have to, for things like parsing value types because if the parse method wasn't allowed to swap out the value type for something else it wouldn't work very well.. But imagine there was some fictitious method in some library you're creating:

public void PoorlyNamedMethod(out SomeClass x)

You can see it's an out, and you can thus know that if you spend hours crunching numbers, creating the perfect SomeClass:

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

Well that was a waste of time, taking all those hours to make that perfect class. It's definitely going to be tossed away and replaced by PoorlyNamedMethod


From the standpoint of a method which receives a parameter, the difference between ref and out is that C# requires that methods must write to every out parameter before returning, and must not do anything with such a parameter, other than passing it as an out parameter or writing to it, until it has been either passed as an out parameter to another method or written directly. Note that some other languages do not impose such requirements; a virtual or interface method which is declared in C# with an out parameter may be overridden in another language which does not impose any special restrictions on such parameters.

From the standpoint of the caller, C# will in many circumstances assume when calling a method with an out parameter will cause the passed variable to be written without having been read first. This assumption may not be correct when calling methods written in other languages. For example:

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

If myDictionary identifies an IDictionary<TKey,TValue> implementation written in a language other than C#, even though MyStruct s = new MyStruct(myDictionary); looks like an assignment, it could potentially leave s unmodified.

Note that constructors written in VB.NET, unlike those in C#, make no assumptions about whether called methods will modify any out parameters, and clear out all fields unconditionally. The odd behavior alluded to above won't occur with code written entirely in VB or entirely in C#, but can occur when code written in C# calls a method written in VB.NET.


If you want to pass your parameter as a ref then you should initialize it before passing parameter to the function else compiler itself will show the error.But in case of out parameter you don't need to initialize the object parameter before passing it to the method.You can initialize the object in the calling method itself.


Mind well that the reference parameter which is passed inside the function is directly worked on.

For example,

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

This will write Dog, not Cat. Hence you should directly work on someObject.


I may not be so good at this, but surely strings (even though they are technically reference types and live on the heap) are passed by value, not reference?

        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"!!!!

This why you need ref if you want changes to exist outside of the scope of the function making them, you aren't passing a reference otherwise.

As far as I am aware you only need ref for structs/value types and string itself, as string is a reference type that pretends it is but is not a value type.

I could be completely wrong here though, I am new.

참고URL : https://stackoverflow.com/questions/388464/whats-the-difference-between-the-ref-and-out-keywords

반응형