16

私は次の方法を持っています.voidがここでは無効であるというコンパイラエラーがあるため、以下の default(void) に入れることができるものがあるかどうかを知りたいです:

private void applyDefaultsIfNecessary(ApplicationConfiguration configuration)
{
    var defaults = new Dictionary<Predicate<ApplicationConfiguration>, Action<ApplicationConfiguration>>()
    {
       // { rule, action } - if rule is true, execute action 
       { (c) => c.ConnectionString == null , (c) => c.ConnectionString = "foo" },
       { (c) => c.OutputExcelFilePath == null, (c) => c.ConnectionString = "bar" },
       { (c) => c.OutputDirectory == null, (c) => c.OutputDirectory = "baz" }

    };

    //Nothing to select, but we want to loop throough the dict and invoke action, if rule is true.
    //It is a pity there is no extension method called DoForEach on collections.
    defaults.Select((item) => item.Key.Invoke(configuration) ? item.Value.Invoke(configuration) : default(void)  );
}

三項演算子の代わりに if-else ステートメントを使用できること (または、ダミー メソッドを呼び出して void を返すことができること) に気付きました。また、Select 拡張メソッドは、void を返すラムダを好みません。型を推測できないと言っているようですが、もちろん、次のように型を指定すると、次のいずれかになります。

defaults.Select<ApplicationConfiguration, void>((item) => { if (item.Key.Invoke(configuration)) item.Value.Invoke(configuration); } );

言語設計の観点から、void または void である変数のデータ型を返すことができる式がない理由に興味がありました。

4

5 に答える 5

14

仕様のセクション 7.1 を参照してください。

【表現は、「なし」に分類される場合があります。これは、式が戻り値の型が void のメソッドの呼び出しである場合に発生します。 何も分類されていない式は、ステートメント式のコンテキストでのみ有効です。

[強調を追加]。

つまり、void を返すメソッド呼び出しである式を使用できるのは、その式がステートメント全体を構成する場合のみです。このような:

M();
于 2010-01-08T19:29:15.650 に答える
11

これは事実上、関数型プログラミングの規則に違反しています。これには、Eric Lippert が List.ForEach について説明したのと同じ欠陥があります。つまり、哲学的にコレクションに副作用を引き起こそうとしています。

Enumerable.Select は、新しいコレクションを返すことを目的としています - 入力をフィルタリングします。コードを実行するためのものではありません。

そうは言っても、次のようにしてこれを回避できます。

defaults.Where(item => item.Key.Invoke(configuration)).ToList().ForEach( item => item.Value.Invoke(configuration));

次のように明確ではありません。

var matches = defaults.Where(item => item.Key.Invoke(configuration));
foreach(var match in matches)
    match.Value.Invoke(configuration);
于 2010-01-08T17:48:14.937 に答える
6

まず、標準の linq クエリ演算子に副作用を入れないようにする必要があります。次に、Select クエリをどこにも列挙していないため、これは実際には機能しません。linq を使用する場合は、次のようにします。

foreach(var item in defaults.Where(i => i.Key.Invoke(configuration)))
{
   item.Value.Invoke(configuration);   
}

あなたの質問に関しては、 void の可能な値はなく、明示的に返すことはできないと確信しています。F# などの関数型言語では、void は「ユニット」に置き換えられます。つまり、可能な値が 1 つのみの型です。必要に応じて、独自のユニット型を作成してそれを返すことができます。この場合、次のようなことができます。

defaults.Select(item => {
    if(item.Key.Invoke(configuration))
    {
        item.Value.Invoke(configuration);
    }
    return Unit.Value;
}).ToList();

しかし、これを行うことは本当にお勧めできません。

于 2010-01-08T17:49:32.077 に答える
3

言語の観点からは、void「存在しない」ことを意味します。これは、存在しない変数を宣言することにどのような価値があるのでしょうか?

ここでの問題は、言語の制限ではなく、2 つの右辺値を必要とする構造 (三項演算子) を使用しているという事実にあります。

率直に言って、無意味な簡潔さを優先して if/else を避けていると思います。置き換えようと思いついたトリックはdefault(void)、他の開発者を混乱させるだけであり、将来、この種のことを気にするのをやめた後でも、あなたを混乱させるだけです。

于 2010-01-08T17:46:30.523 に答える
2

デフォルトは void では機能しません。しかし、それはタイプで動作します。Action クラスは結果を生成しませんが、Func<> オブジェクトは常に結果を返す必要があります。item.Value.Invoke() が返すものは何でも、次のようにそのデフォルトを返します。

デフォルト(オブジェクト)

またはそれが特定のタイプの場合:

デフォルト(いくつかのタイプ)

そのように。

于 2010-01-08T17:39:47.930 に答える