C#7で「Try」メソッドを記述する最もエレガントな方法は何ですか?


21

TryDequeueさまざまな.NET TryParseメソッドに似たパターンを使用するメソッドを持つタイプのQueue実装を記述していoutます。アクションが成功した場合はブール値を返し、パラメーターを使用して実際のデキューされた値を返します。

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

今、私outはできる限りparams を避けるのが好きです。C#7では、変数のデリケートが提供され、それらの操作が簡単になりますが、paramsは有用なツールよりも必要な悪であると考えています。

このメソッドに必要な動作は次のとおりです。

  • デキューするアイテムがあれば、それを返します。
  • デキューするアイテムがない場合(キューが空の場合)、適切に機能するのに十分な情報を発信者に提供します。
  • アイテムが残っていない場合、nullアイテムを返すだけではいけません。
  • 空のキューからデキューしようとしても例外をスローしないでください。

現在、このメソッドの呼び出し元は、ほとんど常に次のようなパターンを使用します(C#7 out変数構文を使用)。

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)機能は憎悪です。
-CodesInChaos

2
私は同様に考えていました...多分<T>、もし1つがOUTパラメータに不利ならオプション<T>
ジョンレイナー

1
@FrustratedWithFormsDesignerよく、私は他のことを「試した」ことはあまりありません(しゃれは大歓迎です)。ValueTupleを返すというアイデアがありましたが、せいぜい改善されたとは思いません。
エリックソンダーガード

@CodesInChaos同じページにいるので、私はここにいます!そして、私はこのアイデアが好きです。時間がある場合は、回答に詳細を記載して、受け入れられるようになりますか?
エリックソンダーガード

回答:


24

オプションタイプを使用します。これは、通常「Some」(値が存在する場合)または「None」(値がない場合)と呼ばれる2つのバージョンを持つタイプのオブジェクトを指します... Just and Nothingと呼ばれています。これらのタイプには、値が存在する場合は値にアクセスし、存在をテストできる関数があり、最も重要なのは、値が存在する場合に値に追加のOptionを返す関数を適用できるメソッドです(通常C#ではこれが必要です)他の言語ではしばしばBindと呼ばれますが、FlatMapと呼ばれます...この名前とタイプのsメソッドを持つことで、LINQステートメントでOptionオブジェクトを使用できるようになるため、C#では名前が重要です。

追加機能には、関連する条件でアクションを呼び出すIfPresentやIfNotPresentなどのメソッドや、OrElse(値が存在しない場合はデフォルト値を置換しますが、それ以外の場合はノーオペレーションです)などがあります。

あなたの例は次のようになります:

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

これはOption(またはMaybe)モナドパターンであり、非常に便利です。既存の実装(例:https : //github.com/nlkl/Optional/blob/master/README.md)がありますが、独自に作成することも難しくありません。

(このパターンを拡張して、メソッドが失敗したときに何もせずにエラー原因の説明を返すようにすることができます...これは完全に達成可能であり、多くの場合いずれかのモナドと呼ばれます。名前が示すように、同じFlatMapを使用できるその場合も作業しやすいパターン)


これは間違いなく良い選択肢です。提案とあなたの考えに感謝します。私は本当にここより多くのアイデアを探求するために探していました。
エリックソンダーガード

2
いいところOption/ Maybe今まで直接異なるケースを処理する必要がなく、それが安全に包まれ、連鎖させることができることを意味モナド(もちろんの一つ、として実装されている場合)、開封されたということです、そして処理し、そしてこのチェーンと処理はLINQクエリ式で実行できます。
ヨルグWミットタグ

3
ところで、.NET ではflatMap/ bindが呼び出さSelectManyれます。
ヨルグWミットタグ

5
別の名前を選択する方がよいのではないかと思います。これを行うTry...と、.NETの命名規則(および潜在的に混乱を招くメンテナー)を破ることになるためです。
ボブ・

大きなポイントだ@Bob。同意する。
エリックソンダーガード

12

与えられた答えは良いものであり、私はそれらのいずれかで行きます。この答えは、いくつかの代替アイデアでいくつかのコーナーを埋めているだけだと考えてください。

  • Message呼び出されたSomeMessageおよびのサブクラスを作成しNoMessageます。メッセージがない場合、およびメッセージがある場合にDequeue戻ることができるようNoMessageになりましたSomeMessage。呼び出し先がどのケースにいるのかを検出したい場合、タイプ検査によって簡単に検出できます。そうでなければNoMessage、そのメソッドのいずれかが呼び出されるたびに何もしないでください。そうすれば、彼らは要求されたものを取得します。

  • 上記と同じですが、Message暗黙的に変換可能にしますbool(または実装しますoperator true / operator false)。これでif (message)、メッセージが良い場合はtrue、悪い場合はfalse と言うことができます。(これはほぼ間違いなく悪い考えですが、完全を期すためにそれを含めています!)

  • C#7にはタプルがあります。(bool, Message)タプルを返します。

  • 作るMessage構造体型とリターンをMessage?


ねえ、答えてくれてありがとう。この質問では、特定の問題の解決策を見つけようとするのと同じくらい、さまざまなアイデアに自分自身をさらけ出そうとしていました。乾杯!
エリックソンダーガード

つまり、Unityで「悪い考え」が使用されている場合、なぜ使用できないのでしょうか。
アルトゥーロトーレスサンチェス

@ArturoTorresSánchez:あなたの質問は、「他の誰かが本当に悪いプログラミングを使っていたのに、なぜ私はできないのですか?」です。質問は一貫性がありません。誰もあなたを他の誰かと同じ悪いプログラミング慣行の使用を止めることはありません。あなたはすぐに行きます。これは、C#での優れたプラクティスではありません。
エリックリッパー

それは頬の舌でした。マークするには「/ s」を使用する必要があると思います。
アルトゥーロトーレスサンチェス

@ArturoTorresSánchez:これは質疑応答サイトです。質問答えを求めている質問だと思います
エリックリッパー

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。既存のタイプの場合、それ自体は不可能かもしれません。
テラスティン

1
@Telastyn:nullを返すだけの場合にも機能します。オプションタイプを使用することもできます。
ジャックB

@JacquesB:しかし、nullが有効なキュー可能アイテムの場合はどうなりますか?
JBSnorro

5

失敗(値なし)が成功と同じくらい一般的なシナリオでは、Try ...メソッド(outパラメーターを使用)に問題はありません。

しかし、別のことをすることを主張する場合は、空のメッセージを返して、それを送信するか(問題ではない)、コンテンツを含むメッセージがあるかどうかを本当に知る必要があるまでifステートメントを延期することができます。

とにかく誰かがテストをしなければならないことに注意してください。私は、質問がいつどこで行われているのかをテストする必要があると主張します。これは、デキューの時点です。


あなたは良いポイントを作ります。何をするにしても、まだテストする必要があります。正直なところ、私はまだこの時点でoutパラメーターを使用している可能性があります(実際、現在、それぞれをいじるために複数の "TryDequeue"実装を公開しています。)そこにあるかもしれない他のオプションについて議論したかっただけです。
エリックソンダーガード
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.