C#ジェネリックのvoid?


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チェックを実行しないのですか?
ジェームズ

戻り値を使用する必要があることに注意してください。プロシージャのような関数を呼び出すことができます。DoSomething(x);代わりにy = DoSomething(x);
Olivier Jacot-Descombes 2012

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 2014

C#はより多くの関数型プログラミング機能をサポートしているため、FP で表すUnitを確認できますvoid。そして、それを使用する正当な理由があります。F#には、まだ.NETがunit組み込まれています。
joe

87

残念ながら、そうではありません。場合は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

2
@Sahuagin:私はそうは思わない-それはこの時点でかなり大きな変化になるでしょう。
Jon Skeet、2012

1
@stannius:いいえ、それは不正確なコードにつながる何かよりも不便なものだと思います。
Jon Skeet 2017

2
「void」を表すために、空の値タイプよりもユニット参照タイプを使用する利点はありますか?空の値タイプは私には適しているように見えます。値を持たず、スペースも取りません。なぜvoidがこのように実装されなかったのかしら。スタックからポップしてもポップしなくても違いはありません(ILでネイティブコードを話すと異なる場合があります)。
Ondrej Petrzilka 2018年

1
@Ondrej:私は以前に空の構造体を使用してみましたが、CLRで空にならない結果になりました...もちろん、それは特殊なケースかもしれません。それを超えて、私はどちらを提案するかわかりません。私はそれについてあまり考えていません。
Jon Skeet 2018年

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は良い提案です。2016年11月の時点で、Unitクラス以外のものを使用していないため、Reactiveフレームワークの可能な限り小さな部分に興味がある場合は、nugetパッケージマネージャーを使用してくださいInstall-Package System.Reactive.Core
DannyMeister

6
ThankYouの名前を「KThx」に変更すると、勝者になります。^ _ ^Kthx.Bye;
LexH 2018

確認するために、何かが欠落していないことを確認します.. Byeゲッターは、直接アクセスに対して重要なものをここに追加しませんか?
Andrew

1
@Andrewは、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)、そのようなの代わりに構造体ValueTuple(0タプル、非ジェネリックバリアント)を使用するの自然なことMyVoidです。そのインスタンスにはToString()を返すaがある"()"ため、ゼロタプルのように見えます。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」を許可する必要があります。ただし、クラスの使用法はmyClass <Nothing>で見栄えが良いです)

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

または

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

1
この値にnullは、欠落または欠落があることを意味しobjectます。値が1つしかNothingないということは、それが他のようには見えないということです。
LexH 2017

それはいい考えですが、@ LexieHankinsに同意します。Nothingプライベート静的フィールドに格納され、プライベートコンストラクターを追加するクラスの一意のインスタンスであることが「何もない」方がよいと思います。nullが依然として可能であるという事実は厄介ですが、うまくいけばC#8で解決されるでしょう
Kirk Woll

学問的に私は違いを理解していますが、違いが重要である非常に特別なケースになります。( "null"の戻り値が特殊なマーカーとして、たとえば何らかのエラーマーカーとして使用される、汎用のnull許容型の関数を想像してください)
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.