런타임에서 ref와 out의 차이점은 무엇입니까?


15

C #은 참조로 전달할 인수를 만들기 위해 refand out키워드를 제공합니다 . 둘의 의미는 매우 유사합니다. 유일한 차이점은 플래그 지정된 변수의 초기화에 있습니다.

  • ref변수를 함수에 전달하기 전에 초기화해야하지만 out그렇지 않습니다.
  • out함수 내에서 변수를 초기화해야하지만 ref그렇지 않습니다.

이 두 키워드의 사용 사례도 거의 동일하며 너무 자주 사용되는 경우 코드 냄새로 간주됩니다 ( TryParseTryGetValue패턴 과 같은 유효한 사용 사례가 있음 ).

이 때문에 누군가가 설명 할 수 있습니다. 왜 C #에 좁은 사용 사례에 대해 두 개의 매우 유사한 도구가 있습니까?

또한 MSDN 에서는 런타임 동작이 서로 다르다고 명시되어 있습니다.

ref 및 out 키워드는 서로 다른 런타임 동작을 유발하지만 컴파일시 메소드 서명의 일부로 간주되지 않습니다.

런타임 동작은 어떻게 다릅니 까?

결론

두 가지 대답이 모두 맞습니다. 감사합니다. 더 명확하기 때문에 jmoreno 님의견을 수락 했습니다.


내 생각 엔 둘 다 refC ++에서 와 같은 방식으로 구현된다 . 문제의 객체 포인터 (또는 프리미티브)에 대한 포인터. 즉 Int32.TryParse(myStr, out myInt)(C #)는 int32_tryParse(myStr, &myInt)(C) 와 같은 방식으로 "실행"됩니다 . 유일한 차이점은 컴파일러가 버그를 방지하기 위해 시행하는 몇 가지 제약 조건입니다. (나는 뒤에서 어떻게 작품에 대해 잘못 될 수 있기 때문에 대답으로이 게시 않을거야,하지만 내가 [그것이 의미하게 때문에] 작동 구상하는 방법입니다)
콜 존슨

답변:


10

이 질문을 받았을 때 MSDN 기사는 미묘하게 잘못되었습니다 (그 이후 수정되었습니다 ). "다른 행동을 유발"하는 대신 "다른 행동을 요구"해야합니다.

특히 동일한 메카니즘 (IL)을 사용하여 동작을 활성화하더라도 컴파일러는 두 키워드에 대해 서로 다른 요구 사항을 적용합니다.


그래서 내 이해는 컴파일러가 out매개 변수를 역 참조하거나 매개 변수를 할당하지 않는 것과 같은 것을 확인 ref하지만 그것이 방출하는 IL은 어느 쪽이든 참조입니다. 그 맞습니까?
앤드류

BTW, 하나는 사실에서이 말할 수 out Tref T같은 VB.Net 또는 C ++ / CLI와 같은 다른 CLI 언어에서 동일하게 렌더링됩니다.
ach

@AndrewPiliser : 맞습니다. 코드가 어느 쪽이든 컴파일된다고 가정하면 (필요하지 않을 때 역 참조하지 않으려는 경우) IL은 동일합니다.
jmoreno

4

다음은 귀하의 질문에 답변 할 수있는 주제에 대한 흥미로운 글입니다.

http://www.dotnetperls.com/ref

관심 장소 :

"ref와 out의 차이점은 Common Language Runtime이 아니라 C # 언어 자체에 있습니다."

최신 정보:

내 작업의 수석 개발자는 @jmoreno의 답변을 확증 했으므로 반드시 읽으십시오!.


따라서 올바르게 이해하면 IL 수준에 차이가 없으므로 런타임 동작이 다를 수 없습니다. 이것은 MSDN의 말과 모순됩니다. 그럼에도 불구하고 재미있는 기사!
Gábor Angyal

@ GáborAngyal 당신은 우연히 MSDN 기사에 대한 링크가 발생하지 않습니까? 의견이 다를 경우 여기에있는 것이 좋을 것입니다
Dan Beaulieu

여기 당신은 간다 : msdn.microsoft.com/en-gb/library/t3c3bfhx.aspx , 그러나 그것은 내 질문에 따라 :)
Gábor Angyal

1
string이 모든 것과 무슨 관련 이 있습니까?
Robert Harvey

그것은 기사의 역설입니다. 두 번째 줄을 제거하도록 업데이트되었습니다.
Dan Beaulieu

2

런타임 ? 물론 없습니다. ref 또는 out 키워드 만 다른 매개 변수가 동일한 메소드를 오버로드 할 수 없습니다.

이것을 컴파일하려고하면 "동일한 서명을 가진 방법이 이미 선언되었습니다"라는 컴파일 오류가 발생합니다.

    private class MyClass
    {
        private void DoSomething(out int param)
        {
        }
        private void DoSomething(ref int param)
        {
        }
    }

이 질문에 대답하기 위해 : "... 왜 C #에 이렇게 좁은 사용 사례를위한 두 개의 매우 유사한 도구가 있습니까?"

코드 가독성과 API 관점에서 보면 큰 차이가 있습니다. API 소비자로서 "out"이 사용될 때 API는 out 매개 변수에 의존하지 않는다는 것을 알고 있습니다. API 개발자로서 나는 "out"을 선호하고 절대적으로 (거의!) 필요할 때만 "ref"를 사용합니다. 훌륭한 토론을 위해이 참조를보십시오 :

/programming/1516876/when-to-use-ref-vs-out

지원 정보 : 다음 방법을 컴파일하고 분해했습니다. ref 및 out 키워드를 사용했지만 (이 예에서는 out) 주소 코드를 제외하고는 어셈블리 코드가 변경되지 않았습니다.

    private class MyClass
    {
        internal void DoSomething(out int param)
        {
            param = 0;
        }
    }

00000000 push ebp
00000001 mov ebp, esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp, 34h
00000009 xor eax, eax
0000000b mov dword ptr [ebp-10h], eax
0000000e mov dword ptr [ebp-1Ch], eax
00000011 mov dword ptr [ebp-3Ch], ecx
00000014 mov dword ptr [ebp-40h], edx
00000017 cmp dword ptr ds : [008B1710h], 0
0000001e je 00000025
00000020 전화 6E6B601E
00000025 nop
param = 0;
00000026 mov eax, dword ptr [ebp-40h]
00000029 xedx, edx
0000002b mov dword ptr [eax], edx
}
0000002d nop
0000002e lea esp, [ebp-0Ch]
00000031 pop ebx
00000032 pop esi
00000033 pop edi
00000034 pop ebp
00000035 ret

어셈블리를 올바르게 읽고 있습니까?


작성하는 내용은 정확하지만이 과부하가 허용되지 않기 때문에 런타임 차이가 없다는 것은 명백한 의미가 아닙니다.
Gábor Angyal

아아-좋은 지적이야! 누구나 위의 코드를 분해하여 옳고 그름을 증명할 수 있습니까? 나는 어셈블리를 읽는 데 끔찍하지만 알고 싶어합니다.
Shmoken

"out"과 "ref"를 사용하여 코드를 디스 어셈블했는데 DoSomething 방법에 대한 차이점을 볼 수 없습니다.
Shmoken
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.