본문 바로가기

TechLog

c#에서의 참조, ref 키워드

c#에서의 참조, ref 키워드

c#에서는 포인터를 사용해서 개체의 주소를 이용/참조하는 대신, ref 키워드를 사용한 참조 기능을 지원함으로써 메서드에 개체를 전달할 수 있도록 합니다. ref 키워드의 사용법을 간단하게 알아보도록 하겠습니다.

 

 

안녕하세요 : )

 

아 티클을 적어나가다보니 의외로 c# 기초 문법에 대한 내용이 별로 없는 듯 해서, 잠시 기초적인 키워드들에 대한 아티클을 작성해보려 합니다. unsafe에 대한 내용을 이해하기 전에 ref를 먼저 이해해야 그 필요성을 알 수 있을텐데, 뭔가 아티클의 순서가 거꾸로 가고 있는 것 같습니다 ^^;

 

개체의 전달은 기초적이지만 중요한 내용이므로, 그 첫 단추를 ref 키워드로 꿰어볼까 합니다.

 

 

c#에서의 개체 전달

 

c# 에서는 unsafe 키워드를 통해 c 스타일의 포인터를 지원하고 있지만, 메서드에서의 인수를 전달하는 방법으로는 값에 의한 전달과 참조에 의한 전달을 권장하고 있다. (함수의 포인터를 전달하는 방법으로는 delegate 사용하고 있으나, 그에 대한 설명은 여기서는 제외한다)

 

값에 의한 전달은 일반적인 메서드의 선언에서 보이는 바와 차이가 없다. 다음과 같이 선언된 메서드와 메서드에 대한 호출은, 값에 의한 전달을 사용하는 코드다

 

 

 

 

void passingByValue(Foo foo)
        {
            // ...
        }
...
passingByValue(foo);

 

 

 

 

 

ref 키워드를 사용해서 참조에 의한 전달을 사용하는 코드는 다음과 같다 :

 

 

 

 

void passingByRef(ref Foo foo)
{
        // ...
}
...
passingByRef(ref foo);

 

 

 

 

 

c/c++에 익숙하다면, &문자를 사용해서 참조를 사용하는 것과 큰 차이가 없다는 점이 눈에 들어올 것이다.

 

 

참조를 사용하는 이유

 

참조를 사용하는 이유를 간략하게 설명하자면, 메서드에 개체를 전달했을 때 개체의 내부 변수 값을 변경하거나 개체 자체를 변경할 수 있도록 하기 위해서이다.

 

위에 적힌 말로만 이해하기는 쉽지 않을 것이다. 실제 코드를 보도록 하자 :

 

 

 

 

using System.Diagnostics;
...
public class Foo
{
    public Foo()
    {
        Debug.WriteLine("Foo() created.");
    }
}
.....
void passingByValue(Foo foo)
{
    Debug.WriteLine(string.Format("in passingByValue() hashcode : {0}", foo.GetHashCode()));
    Debug.WriteLine("and create another one..");
    foo = new Foo();
    Debug.WriteLine(string.Format("in passingByValue() created hashcode : {0}", foo.GetHashCode()));
}
 
void passingByRef(ref Foo foo)
{
    Debug.WriteLine(string.Format("in passingByRef() hashcode : {0}", foo.GetHashCode()));
    Debug.WriteLine("and create another one..");
    foo = new Foo();
    Debug.WriteLine(string.Format("in passingByRef() created hashcode : {0}", foo.GetHashCode()));
}
...
//
이 부분의 코드는 버튼의 click 이벤트 핸들러등에 넣어서 실행시키자
Debug.WriteLine("Create Foo");
Foo foo = new Foo();
Debug.WriteLine(string.Format("init. hashcode : {0}", foo.GetHashCode()));
Debug.WriteLine("");
 
Debug.WriteLine("in passingByValue()");
passingByValue(foo);
Debug.WriteLine(string.Format("and out passingByValue() hashcode : {0}", foo.GetHashCode()));
Debug.WriteLine("");
 
Debug.WriteLine("in passingByRef()");
passingByRef(ref foo);
Debug.WriteLine(string.Format("and out passingByRef() hashcode : {0}", foo.GetHashCode()));

 

 

 

 

 

결과는 다음과 같다 (컴퓨터에 따라 결과가 조금씩 틀려질 수 있다) :

 

 

 

 

Create Foo
Foo() created.
init. hashcode : 13
 
in passingByValue()
in passingByValue() hashcode : 13
and create another one..
Foo() created.
in passingByValue() created hashcode : 14
and out passingByValue() hashcode : 13
 
in passingByRef()
in passingByRef() hashcode : 13
and create another one..
Foo() created.
in passingByRef() created hashcode : 15
and out passingByRef() hashcode : 15

 

 

 

 

 

먼 저 GetHashCode()에 대해 간략히 설명하자면, 특정 타입에 대한 해시 함수를 제공해서 해시 테이블같은 데이터 구조나 해시 알고리즘에 사용할 수 있는 기능을 제공하는 메서드이다. 지금 이 메서드를 사용하는 것은, foo 개체가 존재하고 있는 주소가 같은지를 판단하기 위해서이다. 얻어진 해시 코드가 동일하다면, 같은 주소 공간을 사용하고 있다고 간주할 수 있다.

 

코 드를 따라가보면, 최초에 생성된 foo 개체가 차지한 메모리 공간은 13이다. 이 개체가 passingByValue() 메서드에 넘겨지고, passingByValue() 메서드 안에서 해시 코드를 다시 확인해보면 13이 얻어지므로 passingByValue() 메서드 안에 있는 foo 개체와, 메서드 밖의 foo 개체는 서로 동일한 개체를 가리키는 것이라고 간주할 수 있다. 그 다음 코드에서 개체를 새로 생성했고, 그 개체의 해시 코드는 14가 되는 것을 확인할 수 있다.

 

그 다음 passingByValue() 메서드를 벗어나면? foo 개체의 해시 코드는 13이 되어있다. passingByValue() 안에서 foo 개체로의 새로운 할당은 passingByValue() 메서드 밖에서는 적용되지 않는 것이다.

 

이번에는 passingByRef() 메서드에 foo 개체를 전달했다. 넘겨진 개체의 해시 코드가 똑같이 13으로 얻어졌고, 그 이후의 코드 부분도 passingByValue() 메서드와 차이가 없다. 하지만 passingByRef() 메서드를 벗어나면, foo 개체에는 새로 생성된 개체가 할당되어, 해시 코드 15가 얻어짐을 확인할 수 있다.

 

이런 식으로, 개체를 메서드에 전달한 다음에 개체를 변경하고 싶을 경우에는 ref 키워드를 사용해야만 해당 개체를 변경할 수 있는 것이다

 

 

정리

 

c#의 기본 키워드 중 하나인 ref에 대하여 알아보았습니다. 같은 기능을 하는 키워드로 out이라는 키워드가 있는데, 둘의 차이는 out 키워드로 전달된 개체의 값은 해당 메서드 안에서 변경되는지를 확인한다는 점이 다릅니다.

 

그럼 : )