13

頭を包み込むことができないようです。

私が理解しているように、それは動的にロジックをクラスに追加しています。フレームワーク内のクラスはこれに備えていますか?

クラスを拡張して、拡張機能に機能を追加する必要があるのはなぜですか。私はグローバルにアクセス可能であり、保守がはるかに簡単です。

私は4つのファンクタータイプがあることを読みました:

Comparer
Closure
Predicate
Transformer

私たちはおそらくそれらのそれぞれを処理する必要があります。

ps vbにそのようなものはありますか?

つまり、ラムダ式はファンクターだと思います。これは私のために少し物事をクリアします:)(hehe)

  • ラムダ式はファンクターですか?
  • 匿名関数はファンクターですか?

しかし、私は別のタイプの機能、つまりこれらの機能に遭遇したため、この質問をしました。

delegate void FunctorDelegate(int value);
class Addition {
    FunctorDelegate _delegate;

    public Addition AddDelegate(FunctorDelegate deleg) {
        _delegate += deleg;
        return this;
    }
    public int AddAllElements(IList< int> list) {
        int runningTotal = 0;
        foreach( int value in list) {
            runningTotal += value;
            _delegate(value);
        }
        return runningTotal;
    }
}

そして、これでそれを呼び出します:

 int runningTotal = new Addition()
     .AddDelegate(new FunctorDelegate(
                     delegate(int value) {
                         if ((value % 2) == 1) {
                             runningOddTotal += value;
                         }
                     }))
    .AddDelegate(new FunctorDelegate(
                     delegate(int value) {
                         if ((value % 2) == 0) {
                             runningEvenTotal += value;
                         }
                     }))
    .AddAllElements(list);

したがって、派手なラムダスタイルのものはありません。

今、私はこの例を持っていますが、これが「良い」解決策である理由はまったく明らかではありません。

デリゲート(ファンクター)は、プログラマーのショートカットとして「ほとんどの場合」ラムダ式または無名メソッドとして使用されますか?私が見る限り、それらが実際に問題に対して好まれる選択であるケースはごくわずかです。

4

5 に答える 5

23

異なる言語の用語を混同していると思います。C++またはJavaの意味で「ファンクター」を使用しているようです。たとえば、ウィキペディアのページを参照してください。C++ では、関数呼び出し演算子をオーバーロードするクラスのオブジェクトであるため、関数として使用できますが、状態を伴います。

これは、論理的には、C# (または任意の .NET 言語) のインスタンス メソッドにバインドされたデリゲートと同じことです。

そのようなことを書くには3つの方法があります。まず、通常のメソッドを記述してから、メソッドの名前をデリゲート変数に割り当てます。

void MyMethod() { Console.WriteLine("Hi!"); }

void Foo()
{
    Action a = MyMethod;
    a();
}

次に、C# 2.0 で導入された匿名メソッド構文を使用できます。

void Foo()
{
    Action a = delegate { Console.WriteLine("Hi!"); }
    a();
}

3 番目に、C# 3.0 で導入されたラムダ構文を使用できます。

void Foo()
{
    Action a = () => Console.WriteLine("Hi!");
    a();
}

最後の 2 つの利点は、メソッドの本体が含まれているメソッドのローカル変数を読み書きできることです。

アノンメソッドに対するラムダ構文の利点は、より簡潔であり、パラメーターの型推論を行うことです。

更新:ラムダに対する anon-methods (delegateキーワード) の利点は、パラメーターが必要ない場合はパラメーターを完全に省略できることです。

// correct way using lambda
button.Click += (sender, eventArgs) => MessageBox.Show("Clicked!");

// compile error - wrong number of arguments
button.Click += () => MessageBox.Show("Clicked!");

// anon method, omitting arguments, works fine
button.Click += delegate { MessageBox.Show("Clicked!"); };

nullこれが知っておく価値のある状況を 1 つだけ知っています。それは、イベントを開始する前に確認する必要がないようにイベントを初期化するときです。

event EventHandler Birthday = delegate { };

他の場所で多くのナンセンスを回避します。

最後に、ファンクタには 4 種類あるとおっしゃいましたね。実際、可能性のあるデリゲート型は無数にありますが、一部の作成者はお気に入りを持っている可能性があり、明らかに共通のパターンがいくつかあります。ActionorCommandはパラメータを取らずに を返し、void述語は何らかの型のインスタンスを取り、trueorを返しますfalse

C# 3.0 では、任意の型の最大 4 つのパラメーターでデリゲートを作成できます。

Func<string, int, double> f;  // takes a string and an in, returns a double

Re: 更新された質問

ラムダのユースケースが多いかどうかを尋ねます(と思います)。リストできる以上のものがあります!

ほとんどの場合、シーケンス (オンザフライで計算されるリスト) を操作する大きな式の途中でそれらを目にします。人々のリストがあり、正確に 40 歳の人々のリストが必要だとします。

var exactlyForty = people.Where(person => person.Age == 40);

WhereメソッドはIEnumerable<T>インターフェースの拡張メソッドでありT、この場合はある種のPersonクラスです。

これは、.NET では「Linq to Objects」として知られていますが、他の場所では、シーケンスまたはストリームまたは「遅延」リスト (同じものに対してすべて異なる名前) での純粋な関数型プログラミングとして知られています。

于 2009-06-10T09:51:38.207 に答える
7

.NET 用語では、あなたが説明しているのは- であり、C# だけでなく、.NET 全体に存在すると思います。Delegate

C#の用語では、クロージャーは単なる実装の詳細ですが、これら3つのいずれかになる可能性があるため、「クロージャー」が比較子/述語/トランスフォーマーと同じ「タイプ」になるかどうかはわかりません。

.NET では、デリゲートは主に次の 2 つの方法で使用されます。

  • イベントメカニズムとして
  • 関数型プログラミングを提供する

前者も重要ですが、後者の方が気になるようですね。実際には、それらは単一メソッドのインターフェースのように動作します...考慮してください:

List<int> vals = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> evenVals = vals.FindAll(i => i % 2 == 0); // predicate
List<string> valsAsStrings = vals.ConvertAll(i => i.ToString()); // transformer
// sort descending
vals.Sort((x, y) => y.CompareTo(x)); // comparer

クロージャーは、デリゲートの外側からデリゲート追加のスコープを持ち込むところです。

int max = int.Parse(Console.ReadLine()); // perhaps 6
List<int> limited = vals.FindAll(i => i <= max);

ここでmaxは、クロージャーとしてデリゲートに取り込まれます。

「フレームワーク内のクラスはこれに対応していますか?」に関して - 多くはそうであり、LINQ はこれをさらに広く可能にするのに大いに役立ちます。LINQ は、(たとえば) すべての拡張メソッドを提供します。つまり、デリゲート ベースのアクセスを持たないIEnumerable<T>コレクションは、それらを無料で取得します。

int[] data = { 1,2,3,4,5,6,7,8,9 };
var oddData = data.Where( i => i % 2 == 1 );
var descending = data.OrderBy(i => -i);
var asStrings = data.Select(i => i.ToString());

ここで、 メソッドWhereOrderByメソッドは、デリゲートを受け取る LINQ 拡張メソッドです。

于 2009-06-10T07:57:21.497 に答える
1

用語に興味があります。「ファンクター」という用語の私の自発的な解釈は、それが匿名の方法を指しているということでした。それが私の見解です。

これらは私の典型的な使用法のいくつかです:

比較(通常はリストの並べ替え用):

List<int> ints = new List<int>();
ints.AddRange(new int[] { 9, 5, 7, 4, 3, 5, 3 });
ints.Sort(new Comparison<int>(delegate(int x, int y)
    {
        return x.CompareTo(y);
    }));
// yes I am aware the ints.Sort() would yield the same result, but hey, it's just
// a conceptual code sample ;o)

// and the shorter .NET 3.5 version:
ints.Sort((x, y) =>
{
    return x.CompareTo(y);
});

この特定の並べ替えが1つの場所でのみ発生する場合は、独自のメソッドでそのメソッドのデリゲートを使用するのではなく、このアプローチを比較に使用します。同じ比較を別の場所で使用する可能性が高い場合は、独自の再利用可能な方法で実行できます。

私のかなり一般的な使用法のもう1つは、単体テストで、テストが発生したイベントに依存している場合です。私は、WorkflowFoundationでワークフローを単体テストするときに不可欠であることに気づきました。

WorkflowRuntime runtime = WorkflowHost.Runtime;  
WorkflowInstance instance = runtime.CreateWorkflow(typeof(CreateFile)); 
EventHandler<WorkflowEventArgs> WorkflowIdledHandler = delegate(object sender, WorkflowEventArgs e)
{
    // get the ICreateFileService instance from the runtime  
    ISomeWorkflowService service = WorkflowHost.Runtime.GetService<ISomeWorkflowService>();

    // set the desired file content  
    service.DoSomeWork(instance.InstanceId, inputData);
};  
// attach event handler
runtime.WorkflowIdled += WorkflowIdledHandler;  

instance.Start();  
// perform the test, and then detach the event handler
runtime.WorkflowIdled -= WorkflowIdledHandler; 

この場合instance、単体テストのメソッドスコープで定義されている変数を使用するため、イベントハンドラーを匿名メソッドとして宣言する方が簡単です。代わりに、イベントハンドラーを独自の個別のメソッドとして実装することを選択したinstance場合、おそらく単体テストクラスでは完全な設計とは思えないクラスレベルのメンバーを導入することによって、イベントハンドラーを取得する方法を理解する必要があります。 。

私のコードでこれを見つけるケースは他にもありますが、通常は1つまたは2つの共通点があります。

  • その特定の場所以外の場所からそのコードを参照することに興味はありません
  • このメソッドは、通常のメソッドの範囲外となるデータにアクセスする必要があります
于 2009-06-10T08:56:55.213 に答える
1

本当の答えは、ファンクターは数学オブジェクトの一種であり、さまざまな言語によってさまざまな方法で「具体化」されるということです。たとえば、同じタイプの他のオブジェクトの束を格納する「コンテナ」オブジェクトがあるとします。(たとえば、セットまたは配列) 次に、コンテナー内の各オブジェクトでメソッドを呼び出すことができるように、コンテナーを「マップ」できるメソッドが言語にある場合、コンテナーは functor になります。

言い換えれば、ファンクターは、含まれているものにメソッドを渡すことができるメソッドを持つコンテナーです。

すべての言語には独自の方法があり、使用方法が混同されることがあります。たとえば、C++ では関数ポインターを使用してメソッドの "受け渡し" を表し、関数ポインターを "ファンクター" と呼びます。デリゲートは、渡すことができるメソッドの単なるハンドルです。C++ と同じように、用語を「間違って」使用しています。

Haskellはそれを正しく理解しています。型がファンクター インターフェイスを実装することを宣言すると、マッピング メソッドが取得されます。

関数 (ラムダなど) もファンクターですが、関数を「コンテナー」と考えるのは少し難しい場合があります。要するに、関数は戻り値の周りの「コンテナー」であり、戻り値が (おそらく) 関数の引数に依存するように構築されます。

于 2015-11-24T23:27:53.133 に答える
0

ラムダ式のことだと思います。これらは非常にすばやく記述できる小さな関数であり、特徴的な "=>" 演算子があります。これらは C# 3.0 の新機能です。

この例は、従来の Transformer になります。これを使用するには、Lambda 関数の署名を定義するデリゲートが必要です。

delegate int Transformer(int i);

次に、このデリゲートで Lambda を宣言します。

Transformer sqr = x => x * x;

通常の関数のように使用できます。

Console.WriteLine(sqr(3)); //9

これらは LINQ クエリでよく使用されます。たとえば、並べ替え (比較)、検索 (述語) などです。

「C# ポケット リファレンス」という本 (私の意見では、最も優れていることは別として、ラムダについては非常に良い部分があります。 (ISBN 978-0-596-51922-3)

于 2009-06-10T08:06:15.753 に答える