4

さまざまな場所で小さな操作を繰り返す必要があるメソッドに取り組んでいますが、繰り返されるコードはメソッド専用である必要があります。明らかな解決策は入れ子関数です。しかし、私が何をしようとも、C#コンパイラーは私を怒らせます。

このPerlスニペットとほぼ同じもの:

my $method = sub {
    $helper_func = sub { code to encapsulate };

    # more code

    &$helper( called whenever needed );

    # more code
}

私が話していることであり、C#で達成しようとしていることです。

このコンテキストでは、クラス内の他のメソッドがヘルパー関数にアクセスできないようにする必要があります。この構成をC#で記述する最も論理的な方法は、次のようになります。

var helper = (/* parameter names */) => { /* code to encapsulate */ };

そして実際にコンパイラーにその維持を獲得させます。

このような割り当ては禁止されているため、ラムダの代わりに古いデリゲート(){}構文を使用するのと同じように、メソッド内でデリゲート型を宣言します。ただし、cscで実際に記述できるのは次のとおりです。

private delegate /* return type */ Helper(/* parameters */);
private /* return type */ method(/* parameters */) {

    Helper helper = (/* parameter names */) => { 
        /* code to encapsulate */
    };

    // more code

    helper( /* called whenever needed */ );

    // more code
}

これは、コードのチャンクをコピーして貼り付けたり、パラメーターを手動で編集したりするのではなく、すべて問題ありませんが、メソッドに対してプライベートに保つのではなく、クラスの残りの部分にプライベートデリゲートタイプをリークします。そもそも目的を破る。パラメータにgotoステートメントとローカル変数を使用すると、コードの再利用を犠牲にすることなく、このコンテキストで「ヘルパー」をより適切にカプセル化できます。レジスターを介してパラメーターを渡すことによって関数呼び出しをシミュレートしたい場合は、むしろアセンブラーを使用したいと思います。問題を完全に回避するためにコードをリファクタリングする許容できる方法も見つかりませんでした。

それで、この共通オブジェクト指向言語を強制的に従わせることさえ可能ですか?

4

7 に答える 7

8

実際には、C#でこれを行うことができます。

Func<T1, T2, ..., TReturn> myFunc = (a, b, ...) =>
{
  //code that return type TReturn
};


戻り型voidの匿名メソッドが必要な場合は、Funcの代わりにActionを使用してください。

Action<T1, T2, ...> myAction = (a, b, ...) =>
{
  //code that doesn't return anything
};
于 2010-08-01T16:04:07.287 に答える
6

C#3.5以降を使用している場合は、ラムダとコンビニエンスデリゲート宣言を利用できFunc<>ますAction<>。だから例えば

void DoSomething()
{
  Func<int,int> addOne = (ii) => ii +1;
  var two = addOne(1);
}

できない理由

var addOne = (ii) => ii +1;

同像性のため、ラムダは2つの異なる構成要素、デリゲートと式ツリーとして解釈できます。したがって、宣言で明示的にする必要があります。

于 2010-08-01T16:08:17.163 に答える
4

明示的に入力すると、機能します。

Action<paramType1, paramType2> helperAction = (/* parameter names */) => { /* code to encapsulate */ };
Func<paramType1, paramType2, returnType> helperFunction = (/* parameter names */) => { /* code to encapsulate */ };

varラムダ式が複数の型に評価される可能性があり(デリゲートまたは式ツリーのいずれかを信じていますが、それについては引用しないでください)、この状況のコンパイラーはどちらが意味するのかを推測できないため、機能しません。

于 2010-08-01T16:06:31.057 に答える
2

デリゲートとそのオーバーロードAction<T>を確認することをお勧めします。Func<TResult>あなたはこのようなことをすることができます

static void Main(string[] args)
{
    SomeMethod();
}

private static void SomeMethod()
{
    Action<int> action = (num) => Console.WriteLine(num);

    Enumerable.Range(1,10).ToList().ForEach(action);

    Console.ReadKey();
} 

ここSomeMethodはプライベートで、ローカルのAction<int>デルゲートがあり、それを受け取ってint何かを実行します。

あなたが遭遇した問題はvar、ラムダ式を変数に割り当てるときに暗黙の型付けを使用できない(つまり、使用する)ことができないということだと思います。

于 2010-08-01T16:11:12.793 に答える
1

ラムダまたはデリゲートでキーワードを使用することはできませんvar。どちらも追加のコンテキスト情報を必要とするためです(デリゲートにはリターンタイプが必要であり、ラムダにはリターンタイプとパラメータータイプが必要です)。たとえば、(params) => { code }構文では、パラメータタイプを推測し、タイプを返すことができる必要があります。これを行うには、明示的にタイプを指定します。

ジェネリックSystem.Actionデリゲート型(returns void)は、あなたが試みていることで良い仕事をすることができます:

Action<ArgumentType1, ArgumentType2, ...> myDelegate = (params) => { code };

それ以外の場合System.Funcは、戻り型を持つ、もあり、最後のジェネリック引数として渡す必要があります。

于 2010-08-01T16:10:16.600 に答える
0

それはあなたの隠蔽の定義が何であるかに依存します。

func / actionソリューション(Scottが提案するもののような)

void DoSomething()
{
  Func<int,int> addOne = (ii) => ii +1;
  var two = addOne(1);
}

通常のC#コードを書くときにメソッド定義を隠すような感じがしますが、ILに相当するものを見るときは

//This is pseudo code but comes close at the important parts
public class Class1
    {
        //The actual type is different from this
        private static Func<int, int> myMethod = AnonymousFunction; 

        public void f()
        {
            myMethod(0);
        }

        private static int AnonymousFunction(int i)
        {
            return 1;
        }
    }

したがって、「隠れている」ものの外側からメソッドに本当にアクセスしたい場合は、リフレクションを使用してこれを行うことができます。デリゲートを格納するフィールドに対して生成された実際の名前は、CLRコンテキストで有効なC#bulでは無効ですが、それだけです。フィールドに格納されている通常のデリゲートとしてデリゲートを使用する邪魔になります(つまり、名前を推測した場合:))

于 2010-08-01T16:51:59.827 に答える
-1

実はとても簡単です。メソッドには現在のクラスとは別の責任があるように思われるため(他にこのメソッドを非表示にする理由)、メソッドを独自のクラスに移動し、プライベートにしたい部分を新しいクラスのプライベートメソッドに移動します。

于 2010-08-01T16:02:15.340 に答える