C # 제네릭에서 무효?


94

요청을 받고 응답을 제공하는 일반적인 메서드가 있습니다.

public Tres DoSomething<Tres, Treq>(Tres response, Treq request)
{/*stuff*/}

하지만 항상 내 요청에 대한 응답을 원하지는 않으며 응답을 받기 위해 요청 데이터를 제공하고 싶지는 않습니다. 또한 사소한 변경을 위해 메서드 전체를 복사하고 붙여 넣을 필요가 없습니다. 내가 원하는 것은 다음과 같이 할 수있는 것입니다.

public Tre DoSomething<Tres>(Tres response)
{
    return DoSomething<Tres, void>(response, null);
}

어떤 식 으로든 이것이 가능합니까? 구체적으로 void를 사용하는 것이 작동하지 않는 것 같지만 비슷한 것을 찾고 싶습니다.


1
System.Object를 사용하고 DoSomething (Tres 응답, Treq 요청)에서 null 검사를 수행하지 않는 이유는 무엇입니까?
James

반환 값을 사용해야합니다. 프로 시저와 같은 함수를 호출 할 수 있습니다. DoSomething(x);대신y = DoSomething(x);
올리비에 Jacot-Descombes

1
" 반환 값을 사용할 필요 가 없습니다 ." 라고 말하려고하신 것 같습니다 . @ OlivierJacot-Descombes
zanedp

답변:


95

을 사용할 수는 없지만 사용할 void수 있습니다 object. void함수가 반환되어야 하므로 약간 불편 null하지만 코드를 통합하면 지불하는 데 약간의 비용이 듭니다.

void반환 유형 으로 사용할 수없는이 무능력 은 적어도 부분적으로 Func<...>와 사이의 분할에 대한 책임이 있습니다.Action<...> 은 제네릭 대리자 패밀리 이 있습니다. 반환이 가능 했다면 void모든 Action<X,Y,Z>것이 단순히 Func<X,Y,Z,void>. 불행히도 이것은 불가능합니다.


45
(농담) 그리고 그는 여전히 반환 할 수 void와 그 - 것 - 무효 방법에서 return System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(void));. 하지만 박스형 보이드가 될 것입니다.
Jeppe Stig Nielsen

C #은 더 많은 기능적 프로그래밍 기능을 지원하므로 FP에서 나타내는 Unit 을 살펴볼 수 있습니다 void. 그리고 그것을 사용하는 좋은 이유가 있습니다. F #에서는 여전히 .NET이 unit기본 제공됩니다.

88

아니요, 안타깝게도 아닙니다. 경우 void"진짜"형이었다 (같은 unitF #의는 , 예를 들어) 생활은 여러 가지면에서 훨씬 간단 할 것이다. 특히, 우리는 모두 필요가 없을 것입니다 Func<T>Action<T>그냥 거기있을 거라고 - 가족을 Func<void>대신 Action, Func<T, void>대신 Action<T>

또한 비동기식을 더 간단하게 만들 수 있습니다. 비 제네릭 Task유형 이 전혀 필요하지 않습니다 Task<void>. 그냥 .

불행히도 그것은 C # 또는 .NET 유형 시스템이 작동하는 방식이 아닙니다 ...


4
Unfortunately, that's not the way the C# or .NET type systems work...언젠가는 일이 그렇게 될 수 있다는 희망을 주 셨죠. 마지막 요점은 우리가 그런 식으로 일을 할 가능성이 없다는 것을 의미합니까?
Dave Cousineau 2012

2
@Sahuagin : 나는 그렇지 않다고 생각합니다.
Jon Skeet 2012

1
@stannius : 아니요, 잘못된 코드로 이어지는 것보다 불편한 것 같습니다.
Jon Skeet

2
"무효"를 나타 내기 위해 빈 값 유형보다 단위 참조 유형을 갖는 것이 유리합니까? 빈 값 유형이 나에게 더 적합 해 보이며 값이없고 공간을 차지하지 않습니다. 왜 void가 이렇게 구현되지 않았는지 궁금합니다. 스택에서 팝하거나 팝하지 않아도 차이가 없습니다 (네이티브 코드를 말하면 IL에서는 다를 수 있습니다).
Ondrej Petrzilka

1
@Ondrej : 이전에 빈 구조체를 사용해 보았지만 CLR에서 비어 있지 않습니다 ... 물론 특별한 경우가 될 수 있습니다. 그 외에는 내가 무엇을 제안할지 모르겠습니다. 나는 그것에 대해 많이 생각하지 않았습니다.
Jon Skeet

27

여기에 할 수있는 일이 있습니다. @JohnSkeet가 C #에는 단위 유형이 없다고 말했듯이 직접 만드십시오!

public sealed class ThankYou {
   private ThankYou() { }
   private readonly static ThankYou bye = new ThankYou();
   public static ThankYou Bye { get { return bye; } }
}

이제 항상 Func<..., ThankYou>대신 사용할 수 있습니다.Action<...>

public ThankYou MethodWithNoResult() {
   /* do things */
   return ThankYou.Bye;
}

또는 Rx 팀에서 이미 만든 것을 사용 하십시오 . http://msdn.microsoft.com/en-us/library/system.reactive.unit%28v=VS.103%29.aspx


System.Reactive.Unit은 좋은 제안입니다. 당신은 당신이 단위 클래스 이외를 사용하지 않기 때문에, 가능한 한 반응성 프레임 워크의 작은 조각으로 점점 관심이 있다면 년 11 월 2016대로, nuget 패키지 관리자 사용Install-Package System.Reactive.Core
DannyMeister

6
ThankYou의 이름을 "KThx"로 변경하면 승자가됩니다. ^ _ ^Kthx.Bye;
LexH

그냥 내가 뭔가를 놓치고 있지 않은지 확인하기 위해 .. Bye 게터는 여기에 직접 액세스를 통해 중요한 것을 추가하지 않습니다.
Andrew

1
@Andrew는 C # 코딩의 정신을 따르고 싶지 않다면 바이 ​​게터가 필요하지 않습니다. C # 코딩은 베어 필드를 노출해서는 안된다고 말합니다
Trident D' Gao

16

Object다른 사람들이 제안한대로 간단히 사용할 수 있습니다 . 또는 Int32나는 약간의 사용을 본 적이 있습니다. 를 사용 Int32하면 "더미"번호 (사용 0)가 도입 되지만 적어도 크고 이국적인 객체를 Int32참조에 넣을 수는 없습니다 (구조물은 봉인 됨).

자신 만의 "void"유형을 작성할 수도 있습니다.

public sealed class MyVoid
{
  MyVoid()
  {
    throw new InvalidOperationException("Don't instantiate MyVoid.");
  }
}

MyVoid참조가 허용되지만 (정적 클래스가 아님) null. 인스턴스 생성자는 비공개입니다 (누군가 리플렉션을 통해이 비공개 생성자를 호출하려고하면 예외가 발생합니다).


값 튜플 (2017, .NET 4.7)이 도입되었으므로 이러한 .NET Framework 대신 구조체ValueTuple (0- 튜플, 비 제네릭 변형) 를 사용 하는 것이 당연 할 수 있습니다 MyVoid. 인스턴스에는를 ToString()반환 "()"하는가 있으므로 제로 튜플처럼 보입니다. 현재 버전의 C #에서는 ()코드 의 토큰 을 사용하여 인스턴스를 가져올 수 없습니다 . 대신 default(ValueTuple)또는 default(문맥에서 유형을 유추 할 수있는 경우) 사용할 수 있습니다.


2
이것의 또 다른 이름은 null 객체 패턴 (디자인 패턴)입니다.
Aelphaeis 2014 년

@Aelphaeis이 개념은 null 개체 패턴과 약간 다릅니다. 여기서 요점은 제네릭과 함께 사용할 수있는 유형을 갖는 것입니다. null 객체 패턴의 목표는 특별한 경우를 나타 내기 위해 null을 반환하는 메서드를 작성하지 않고 대신 적절한 기본 동작으로 실제 객체를 반환하는 것입니다.
Andrew Palmer

8

위의 Aleksey Bykov의 아이디어가 마음에 들지만 약간 단순화 할 수 있습니다.

public sealed class Nothing {
    public static Nothing AtAll { get { return null; } }
}

Nothing.AtAll이 null을 줄 수없는 명백한 이유가없는 것처럼

동일한 아이디어 (또는 Jeppe Stig Nielsen의 아이디어)도 형식화 된 클래스와 함께 사용하기에 좋습니다.

예를 들어 형식이 일부 메서드에 인수로 전달 된 프로 시저 / 함수에 대한 인수를 설명하는 데만 사용되며 자체적으로 인수를 사용하지 않는 경우입니다.

(그래도 더미 래퍼를 만들거나 선택적인 "Nothing"을 허용해야합니다. 그러나 IMHO 클래스 사용은 myClass <Nothing>으로 멋지게 보입니다.)

void myProcWithNoArguments(Nothing Dummy){
     myProcWithNoArguments(){
}

또는

void myProcWithNoArguments(Nothing Dummy=null){
    ...
}

1
이 값 null은 누락되거나 없음을 의미 object합니다. 에 대해 하나의 값만 있으면 Nothing다른 것과 같이 보이지 않습니다.
LexH

좋은 생각이지만 @LexieHankins에 동의합니다. "아무것도 전혀"가 Nothing개인 정적 필드에 저장된 클래스의 고유 한 인스턴스이고 개인 생성자를 추가하는 것이 더 낫다고 생각 합니다. 널 (null)이 여전히 가능하다는 사실은 성가신,하지만 희망 C # 8.으로 해결 될 것입니다
커크 WOLL

학문적으로 나는 그 차이를 이해하지만 그 차이가 중요한 경우는 매우 특별한 경우가 될 것입니다. ( "null"의 반환이 특수 마커, 예를 들어 일종의 오류 마커로 사용되는 일반적인 nullable 유형의 함수를 상상해보십시오.)
Eske Rahn

4

void유형이지만 메서드의 반환 유형으로 만 유효합니다.

이 제한을 피할 방법은 없습니다 void.


1

현재 내가하는 일은 개인 생성자로 사용자 지정 봉인 된 형식을 만드는 것입니다. 상황이 올바르지 않은지 확인하기 위해 런타임까지 얻을 필요가 없기 때문에 c-tor에서 예외를 던지는 것보다 낫습니다. 한 번도 할당 할 필요가 없기 때문에 정적 인스턴스를 반환하는 것보다 미묘하게 낫습니다. 호출 측에서 덜 장황하기 때문에 정적 null을 반환하는 것보다 미묘하게 낫습니다. 호출자가 할 수있는 유일한 일은 null을주는 것입니다.

public sealed class Void {
    private Void() { }
}

public sealed class None {
    private None() { }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.