C# 키워드 out에 대한 질문

안녕하세요. :smile:

C# 키워드 out에 대한 질문입니다.

저는 out 키워드를 TryParse 기능을 통해 처음 접했고
값을 받는 문법이 조금 독특하여 좀 더 알아봤었는데요.

메서드가 out 인자를 받게 된다면 그 메서드는
무조건 return 전에 out 값을 할당해야 하는 것으로 알고 있습니다.

그리고 제 생각에는 TryParse와 유사한 기능(처리 유무에 따른 로직)을 처리할 때
이 키워드를 사용하는 것이 좋다고 생각되어 이와 비슷한 상황에 자주 키워드를 사용 중입니다.

하지만 어떠한 상황에서 out 키워드를 사용하는 것이 적절한지는 잘 모르겠습니다.

혹시 적극적으로 out 키워드를 사용하시는 분이 계신다면
노하우와 이에 대한 생각도 듣고 싶습니다!

그리고 out 키워드에 대한 비판적인 의견도 알고 싶습니다.

읽어주셔서 감사합니다!

좋아요 4

예외처리의 간결함의 입장에서 Try~(+out) 와같은 함수를 선호하기 시작했습니다.
단 이경우 함수가 예외처리를 완벽하게 해야하며 값을 안전하게 사용할수 있다란 보장값을 true를
통해 전달해줄수있기때문에(함수는 하나밖에 반환못하는 단점을 극복)
호출자가 null체크,예외발생코드등을 작성안해도 되기때문입니다.

장점 1줄 요약 :호출자에게 예외처리를 떠넘기지 않을수 있다.

out 키워드단점이라기보다, 함수가 인자값을 변형시킬수 있다란것은 함수형프로그래밍 불변성( 객체를 변하게함으로 발생하는 여러가지문제) 과 비교해야하는 것으로 보입니다.

단점 1줄 요약 : 복잡하게 사용하면 값이 어떻게 바뀔지 예측할수 없다.

좋아요 5
  1. out은 ref, in과 마찬가지로 값형을 반환할 때 유용합니다. 사이즈가 있는 값형을 반환할 때 in/out/ref 를 씁니다.
  2. Try~ 로 시작하는 메소드는 예외를 발생하지 않고 반환값을 bool형인 성공/실패로 처리하고자 할 때 좋습니다. 반환값을 out으로 인자로 반환합니다.

인자에 out을 쓰게 되면, 코딩하다보면 복수개의 값을 반환할 때 여러개의 인자에 out을 쓰고 싶은 유혹?이 생기는데요, 함수는 의미있는 하나의 결과값을 반환하는게 잘 설계된 것이라 할 수 있으므로, 그런것만 유의하면, out 인자는 상황에 따라 적극적으로 써도 무방할 듯 합니다.

좋아요 3

^^; 댓글달고 나서 @psmon님 글을 봤네요;; 요약 장/단점 인상깊습니다 ^^

좋아요 3

감사합니다. 저도 게시하고 나서 디모이님 뎃글을 보았네요비슷한 답변인거같은데 다르게 표현이 되는군요 ^^

좋아요 2

지원하는 키워드니까 적절하게 써주면 빠른 유지보수에는 좋을 것 입니다.

하지만 장기적으로 볼때는 좋은 방법은 아니라 생각합니다. input 객체를 받아 output 객체를 반환하는 방식이 개방 폐쇄 원칙 을 가장 잘 지키는 메소드 모델링이라고 생각합니다. out을 통해 메소드를 수정하여 유지보수를 하게되면 인터페이스를 쓰는 경우 같이 수정해줘야하며 다른 메서드를 사용하는 부분까지 수정해야 합니다. 반면 input 객체를 만들어서 개발한다면, 처음 개발할 때는 POCO 모델 클래스를 만들어줘야하니까 귀찮을 수 있고 파일이 늘어나겠지만, 메서드 수정없이 Input 객체에 Property만 추가해준다면 input 요소의 수도 얼마든지 늘릴 수 있습니다.

input output 객체 없이 원시자료형으로만 사용하는 메소드라면야 사용해도 무방할 듯하지만 사용자 객체를 받는 경우라면 남발하는 것은 개방 폐쇄 원칙을 어길 위험이 있어 지양 해야한다고 생각합니다.

좋아요 3

out 인자는… c++ 과의 호환을 위해 만들어진 키워드입니다. (Interop Service)

해서 c++과 호환하려는 목적 (PInvoke)이 아니면 사용을 하지 않는 것이 좋습니다.
@Vincent 님 의견대로…

이를 위해 .Net은 Tuple로의 반환을 가능하게 했습니다.

out 키워드는 실제로 MS에서도 일반적인 사용을 권장하지 않습니다.

좋아요 4

아 그런게 있었군요…알려주셔서 감사합니다!!!

좋아요 1

@psmon @dimohy 저도 한번은 크롤링 관련해서 out 키워드를 요긴하게 활용했습니다. :smile:

bool TryFindElement(IWebElement target out IWebElement ele)
{
    try 
    {
        ele = target.FindElement(...); 
        return true;
    }
    catch 
    {
        return false;
    }
}

그리고 좋은 답변 주셔서 모두 감사드립니다!

@Vincent @SangHyeon.Kim

그리고 간단한 예시 코드도 제안해주시면 좋을 것 같습니다!!

부탁드려도 될까요?

좋아요 1

별건 아니고 제가 써놓은 의미 그대로 입니다.

public int intReturn(string asdf, out int rtnOut)
{
    ...생략...
    rtnOut = 10;

    return 20;
}

// 유지 보수 발생1
public int intReturn(string asdf, out int rtnOut, out int rtnOut1)
{
    ...생략...
    rtnOut = 10;
    rtnOut1 = 30;

    return 20;
}

// 유지 보수 발생2
public int intReturn(string asdf, out int rtnOut, out int rtnOut1, out int rtnOut2)
{
    ...생략...
    rtnOut = 10;
    rtnOut1 = 30;
    rtnOut2 = 40;

    return 20;
}

이런식으로 하면서 메서드에서 계속 리턴에 데이터를 추가할 일이 생기면 out out out out…늘어나겠죠.
그냥 그렇게 안하기 위해 클래스로 만들어서 코드 가독성을 높이고 개방폐쇄 원칙을 지키는 것입니다.

개방 폐쇄 원칙에 대해 이론을 읽어보시면 메소드에 대하여 깔끔하게 닫히고 깔끔하게 확장되는 구조인데요. 그럴려면 Input 클래스와 Output 클래스를 만드는 방법말고는 없습니다.

public class reqInput
{
     public int data1 {get; set;}
     public string data2 {get; set;}
}

public class resOutput
{
    public int rtnOut {get; set;}
}

public resOutput intReturn(reqInput input)
{
    ...생략...
    return resOutput;
}

//유지보수 발생1
public class reqInput
{
     public int data1 {get; set;}
     public string data2 {get; set;}
     public int data3 {get;set;}
}

public class resOutput
{
    public int rtnOut {get; set;}
}

public resOutput intReturn(reqInput input)
{
    ...생략...
    return resOutput;
}

//유지보수 발생2
public class reqInput
{
     public int data1 {get; set;}
     public string data2 {get; set;}
     public int data3 {get;set;}
     public int data4 {get;set;}
}

public class resOutput
{
    public int rtnOut {get; set;}
}

public resOutput intReturn(reqInput input)
{
    ...생략...
    return resOutput;
}

보시면 두번째 예제는 input class에 property만 추가해주고 메서드는 변형이 없죠.

그냥 이렇게하면 말씀드린대로 처음은 귀찮을 수 있어도 유지보수가 용이하다는 것입니다. intReturn 메소드를 참조하는 Interface나 class에 대하여 일일히 수정하고 다니지 않아도 input 모델을 받고 있으므로 거기에 property로 요소만 추가해주면 쓸 일이 없는 곳에서는 데이터를 안쓰면 되는거고, 필요한 곳에서는 input class로 인해 확장성이 용이해졌으니 거기에 추가만 해서 쓰면 되는 것입니다.

개방 폐쇄 원칙(Open-Close Principle)

좋아요 4

저 역시 윗분들과 생각이 비슷합니다.

Try… 시리즈 말고는 out 키워드를 거의 사용하지 않는데,
그 이유가 out 키워드를 쓰면 거의 대부분의 상황에서 메서드 하나가 하나의 기능이 아니게 되더군요.

비슷한 맥락으로 ref도 무분별한 사용은 지양하고 있습니다.

좋아요 2

예전에는 메서드 내부의 상태를 명시적으로 output 하기 위해 간단하게 쓰였는데
이제는 복잡하기 만한 out 키워드는 사용을 지양해야 하지 않을까 싶습니다.
결과가 필요하면 클래스로 받던지, 아니면 튜플을 이용하는 방법으로요.
개인적으로 튜플이 지원되면서 레거시 문법으로 전락했다고 생각됩니다.

좋아요 2

관련하여 블로그 글을 작성해본 적이 있어 아래 공유드립니다.

좋아요 4

메소드 인자 in/out이라 공유주신 주제와 다른 내용 같지만, 공유주신 내용이 굉장히 유용하네요! 감사합니다.

좋아요 2

메서드의 out 키워드랑 햇갈리기는 하더라고요. 그래도 메서드의 out 키워드도 자식 클래스도 반환 가능하다는 의미로도 사용되니까 뭐… 의미는 동일한데 동작이 다른 거 같아요.

좋아요 4

우연찮게 Covariance 에 대한 글이 잘 이해가 되지 않아 찾아보던 차에 작성하신 블로그 글을 보고 닷넷데브 글을 보러 왔는데 마침 링크가 있네요…
다시금 좋은 글 감사드립니다.

좋아요 3