C # 7에서 "Try"메소드를 작성하는 가장 우아한 방법은 무엇입니까?


21

TryDequeue다양한 .NET TryParse메소드 와 유사한 패턴을 사용 하는 메소드 가있는 큐 구현 유형을 작성 중입니다. 여기서 조치가 성공하면 부울 값 out을 리턴하고 매개 변수를 사용 하여 실제 큐에 넣은 값을 리턴하십시오.

public bool TryDequeue(out Message message) => _innerQueue.TryDequeue(out message);

이제 out가능할 때마다 매개 변수 를 피하고 싶습니다 . C # 7을 사용하면 변수를 쉽게 제거 할 수있는 다양한 분리가 가능하지만, 여전히 유용한 도구보다 매개 변수가 필요한 악을 더 고려합니다.

이 방법에서 원하는 동작은 다음과 같습니다.

  • 대기열에 넣을 품목이 있으면 반품하십시오.
  • 대기열에서 비울 항목이 없으면 발신자에게 적절한 조치를 취하기에 충분한 정보를 제공하십시오.
  • 남은 항목이 없으면 null 항목 만 반환하지 마십시오.
  • 빈 대기열에서 대기열을 해제하려고 할 때 예외를 던지지 마십시오.

현재이 메소드의 호출자는 거의 항상 다음과 같은 패턴을 사용합니다 (C # 7 출력 변수 구문 사용).

if (myMessageQueue.TryDequeue(out Message dequeued))
    MyMessagingClass.SendMessage(dequeued)
else
    Console.WriteLine("No messages!"); // do other stuff

어느 쪽도 최악이 아닙니다. 그러나 나는 도울 수는 없지만 더 좋은 방법이있을 것이라고 생각합니다 (나는 없을 수도 있음을 완전히 기꺼이 인정합니다). 호출자가 원하는 경우 값이 있으면 조건부와 함께 흐름을 분류하는 방법이 싫습니다.

이 같은 "시도"동작을 달성하기 위해 존재하는 다른 패턴은 무엇입니까?

맥락에서 볼 때이 방법은 VB 프로젝트에서 호출 될 수 있으므로 두 가지 모두에서 잘 작동하는 보너스 포인트입니다. 그러나이 사실은 무게가 매우 적습니다.


9
Option<T>구조체를 정의 하고 반환하십시오. 그 bool Try(..., out data)기능은 혐오입니다.
코드 InChaos

2
비슷한 생각을하고있었습니다 ... OUT <매개 변수에 불리한 경우 <T>, 옵션 <T>.
Jon Raynor

1
@FrustratedWithFormsDesigner 잘, 나는 다른 것들을 생각하지 않았습니다. 나는 ValueTuple을 반환 할 생각이 있었지만 기껏해야 그게 크게 개선되지 않았다고 생각합니다.
Eric Sondergard

@CodesInChaos 우리는 같은 페이지에 있습니다. 그리고 나는이 아이디어를 좋아한다. 당신은 시간이 있다면, 내가 받아 들일 수 있도록 좀 더 자세하게 답변하고 싶습니까?
Eric Sondergard

답변:


24

일반적으로 "일부"(값이있는 경우) 또는 "없음"(값이없는 경우)이라고하는 두 가지 버전이있는 유형의 객체를 말하는 옵션 유형을 사용하십시오. 그들은 Just and Nothing이라고 불립니다. 이러한 유형에는 존재하는 경우 값에 액세스하고 존재 여부를 테스트 할 수있는 함수가 있으며, 가장 중요한 것은 값에 존재하는 경우 추가 옵션을 반환하는 함수를 적용 할 수있는 방법입니다 (일반적으로 C #에서는 다른 언어에서는 종종 대신 바인드라고하지만 FlatMap이라고합니다.이 이름과 유형의 메소드를 사용하면 LINQ 문에서 Option 개체를 사용할 수 있기 때문에 C #에서 이름이 중요합니다.

추가 기능에는 관련 조건에서 조치를 호출하기위한 IfPresent 및 IfNotPresent와 같은 메소드 및 OrElse (값이없는 경우 기본값을 대신 함) 등의 메소드가 포함될 수 있습니다.

그런 다음 예제는 다음과 같습니다.

myMessageQueue.TryDeque()
    .IfPresent( dequeued => MyMessagingClass.SendMessage(dequeued))
    .IfNotPresent (() =>  Console.WriteLine("No messages!")

이것은 옵션 (혹은 어쩌면) 모나드 패턴이며 매우 유용합니다. 기존 구현 (예 : https://github.com/nlkl/Optional/blob/master/README.md )이 있지만 직접 구현 하기는 어렵지 않습니다.

(메소드가 실패했을 때 아무것도 아닌 오류 원인에 대한 설명을 반환하도록이 패턴을 확장 할 수 있습니다. 이것은 완전히 달성 가능하며 종종 모나드라고합니다. 이름에서 알 수 있듯이 동일한 FlatMap을 사용할 수 있습니다 이 경우에도 작업하기 쉬운 패턴)


제안과 의견에 감사드립니다. 나는 정말로 더 많은 아이디어를 탐구하려고했습니다.
Eric Sondergard

2
좋은 일에 대한이 Option/ Maybe지금 바로 다른 경우를 처리 할 필요없이, 안전하게, 미개봉, 체인 포장 할 수있는 수단을 (물론, 하나 구현 경우) 및 처리가 모나드는 점이다, 그리고 이 체인과 LINQ 쿼리 식을 사용하여 처리 할 수 ​​있습니다.
Jörg W Mittag

3
그런데 flatMap/ bindSelectMany.NET에서 호출 됩니다.
Jörg W Mittag

5
다른 이름을 선택하는 것이 더 좋은 아이디어인지 궁금합니다.이를 수행하면 Try....NET (및 잠재적으로 혼란스러운 관리자)의 명명 규칙을 위반 하기 때문입니다.
Bob

@Bob 좋은 지적입니다. 동의한다.
Eric Sondergard

12

주어진 대답은 좋으며 그중 하나와 함께 갈 것입니다. 이 답변이 몇 가지 대안 아이디어로 채워져 있다고 생각하십시오.

  • 의 서브 클래스 확인 Message전화 SomeMessageNoMessage. 메시지가없는 경우 및 메시지가있는 경우 Dequeue반환 할 수 있습니다 . 수신자가 자신의 케이스를 감지하려고하면 유형 검사를 통해 쉽게 해결할 수 있습니다. 그렇지 않다면, 메소드가 호출 될 때마다 아무 것도하지 않으면 요청한 것을 얻습니다.NoMessageSomeMessageNoMessage

  • 위와 동일하지만 Message암시 적으로 변환 가능 bool(또는 구현 operator true / operator false)합니다. 이제 if (message)메시지가 좋으면 말을 하고 사실이면 틀릴 수 있습니다. (이것은 거의 확실히 나쁜 생각이지만 완전성을 위해 포함시킵니다!)

  • C # 7에는 튜플이 있습니다. (bool, Message)튜플을 반환합니다 .

  • 확인 Message구조체 유형 및 반환 Message?.


대답 해줘서 고마워 이 질문으로 나는 내 특정 문제에 대한 해결책을 찾으려고 노력했을 때만 큼 다른 아이디어에 나 자신을 노출하려고 노력했습니다. 건배!
Eric Sondergard

당신의 "나쁜 생각"이 Unity에 의해 사용된다면 왜 우리는 그것을 사용할 수 없습니까?
Arturo Torres Sánchez

@ ArturoTorresSánchez : 귀하의 질문은 "다른 누군가가 정말 나쁜 프로그래밍 연습을 사용했기 때문에 왜 안될까요?"입니다. 문제는 일관되지 않습니다. 아무도 당신이 다른 사람과 같은 나쁜 프로그래밍 관행을 사용하는 것을 막지 않습니다. 당신은 바로 앞서갑니다. C #에서는 좋은 연습이 아닙니다.
Eric Lippert

뺨에 혀였습니다. 표시하려면 "/ s"를 사용해야한다고 생각합니다.
Arturo Torres Sánchez

@ ArturoTorresSánchez : 이것은 질문과 답변 사이트입니다; 내가 할 사항을 고려 질문 찾고 있습니다 답변을 .
Eric Lippert

10

C # 7에서는 패턴 일치를 사용하여보다 우아한 방식으로 패턴 일치를 달성 할 수 있습니다.

if (myMessageQueue.TryDequeue() is Message dequeued) 
{
     MyMessagingClass.SendMessage(dequeued)
} 
else 
{
    Console.WriteLine("No messages!"); // do other stuff
}

그러나이 특별한 경우에는 큐를 반복적으로 폴링하는 대신 이벤트를 사용합니다.


3
비록 구현과 "아무것도없는"구현으로 TryDequeue마커 인터페이스를 반환 할 필요 Message는 없습니까? 물론, 그것은 호출 사이트에서보다 우아하지만 실제 코드에서 3 가지 구현을 구현하고 있습니다 Message. 기존 유형 인 경우 자체 구현이 불가능할 수 있습니다 .
Telastyn

1
@ Telastyn : 그냥 null을 반환하는 경우에도 작동합니다 . 옵션 유형을 사용할 수도 있습니다.
JacquesB

@JacquesB : 그러나 null이 유효한 대기열에있는 항목이면 어떻게됩니까?
JBSnorro

5

실패 (값 없음)가 성공만큼 일반적인 시나리오에 대해서는 Try ... 메소드 (out 매개 변수가 있음)에는 아무런 문제가 없습니다.

그러나 다른 방식으로 일한다고 주장하는 경우 빈 메시지를 반환하고 더 이상 문제가 아닌 메시지를 보내거나 내용이있는 메시지가 있는지 여부를 알아야 할 때까지 if 문을 연기 할 수 있습니다.

누군가는 어쨌든 테스트를 수행해야합니다. 나는 질문이 언제 그리고 어디에서 최신인지 시험이 이루어져야한다고 주장한다. 이것은 dequeing 할 때입니다.


당신은 좋은 지적을합니다. 당신이 무엇을하든 여전히 테스트를해야합니다. 솔직히, 나는 여전히이 시점에서 매개 변수를 사용하지 않을 수도 있습니다 (사실 현재 여러 개의 "TryDequeue"구현을 현재 노출시키고 있습니다). 나는 정말로 다른 옵션에 대해 이야기하고 싶었습니다.
Eric Sondergard
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.