71

Func<>とAction<>について私が見つけることができるすべての例は、それらが技術的にどのように機能するかを見る以下の例のように単純ですが、以前は解決できなかった、または解決できた問題を解決する例でそれらを使用してもらいたいと思いますより複雑な方法でのみ解決されます。つまり、それらがどのように機能するかを知っており、それらが簡潔で強力であることがわかります。したがって、それらがどのような問題を解決し、どのように使用できるかについて、より広い意味で理解したいと思います。アプリケーションの設計。

実際の問題を解決するために、どのように(パターン)Func<>とAction<>を使用しますか?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestFunc8282
{
    class Program
    {
        static void Main(string[] args)
        {
            //func with delegate
            Func<string, string> convert = delegate(string s)
            {
                return s.ToUpper();
            };

            //func with lambda
            Func<string, string> convert2 = s => s.Substring(3, 10);

            //action
            Action<int,string> recordIt = (i,title) =>
                {
                    Console.WriteLine("--- {0}:",title);
                    Console.WriteLine("Adding five to {0}:", i);
                    Console.WriteLine(i + 5);
                };

            Console.WriteLine(convert("This is the first test."));
            Console.WriteLine(convert2("This is the second test."));
            recordIt(5, "First one");
            recordIt(3, "Second one");

            Console.ReadLine();

        }
    }
}
4

9 に答える 9

57

また、switchステートメントのリファクタリングにも便利です。

次の(単純ではありますが)例を見てください。

public void Move(int distance, Direction direction)
{
    switch (direction)
    {
        case Direction.Up :
            Position.Y += distance;
            break;
        case Direction.Down:
            Position.Y -= distance;
            break;
        case Direction.Left:
            Position.X -= distance;
            break;
        case Direction.Right:
            Position.X += distance;
            break;
    }
}

アクションデリゲートを使用すると、次のようにリファクタリングできます。

static Something()
{
    _directionMap = new Dictionary<Direction, Action<Position, int>>
    {
        { Direction.Up,    (position, distance) => position.Y +=  distance },
        { Direction.Down,  (position, distance) => position.Y -=  distance },
        { Direction.Left,  (position, distance) => position.X -=  distance },
        { Direction.Right, (position, distance) => position.X +=  distance },
    };
}

public void Move(int distance, Direction direction)
{
    _directionMap[direction](this.Position, distance);
}
于 2009-10-08T15:12:42.127 に答える
15

linqを使用します。

List<int> list = { 1, 2, 3, 4 };

var even = list.Where(i => i % 2);

のパラメータはWhereですFunc<int, bool>

ラムダ式は、C#の私のお気に入りの部分の1つです。:)

于 2009-10-08T12:09:52.447 に答える
14

私はいつもActionFuncデリゲートを使用しています。私は通常、スペースを節約するためにラムダ構文でそれらを宣言し、主に大きなメソッドのサイズを減らすためにそれらを使用します。メソッドを確認すると、類似したコードセグメントが目立つ場合があります。そのような場合、私は同様のコードセグメントをActionまたはにまとめFuncます。デリゲートを使用すると、冗長なコードが減り、コードセグメントに適切な署名が付けられ、必要に応じてメソッドに簡単に昇格できます。

私はDelphiコードを書いていましたが、関数内で関数を宣言できました。ActionとFuncは、c#でこれと同じ動作を実現します。

デリゲートを使用したコントロールの再配置の例を次に示します。

private void Form1_Load(object sender, EventArgs e)
{
    //adjust control positions without delegate
    int left = 24;

    label1.Left = left;
    left += label1.Width + 24;

    button1.Left = left;
    left += button1.Width + 24;

    checkBox1.Left = left;
    left += checkBox1.Width + 24;

    //adjust control positions with delegate. better
    left = 24;
    Action<Control> moveLeft = c => 
    {
        c.Left = left;
        left += c.Width + 24; 
    };
    moveLeft(label1);
    moveLeft(button1);
    moveLeft(checkBox1);
}
于 2009-10-08T13:59:48.170 に答える
9

私がそれを使用する1つのことは、同じ入力が与えられても決して変更されない高価なメソッド呼び出しのキャッシングです。

public static Func<TArgument, TResult> Memoize<TArgument, TResult>(this Func<TArgument, TResult> f)
{
    Dictionary<TArgument, TResult> values;

    var methodDictionaries = new Dictionary<string, Dictionary<TArgument, TResult>>();

    var name = f.Method.Name;
    if (!methodDictionaries.TryGetValue(name, out values))
    {
        values = new Dictionary<TArgument, TResult>();

        methodDictionaries.Add(name, values);
    }

    return a =>
    {
        TResult value;

        if (!values.TryGetValue(a, out value))
        {
            value = f(a);
            values.Add(a, value);
        }

        return value;
    };
}

デフォルトの再帰フィボナッチの例:

class Foo
{
  public Func<int,int> Fibonacci = (n) =>
  {
    return n > 1 ? Fibonacci(n-1) + Fibonacci(n-2) : n;
  };

  public Foo()
  {
    Fibonacci = Fibonacci.Memoize();

    for (int i=0; i<50; i++)
      Console.WriteLine(Fibonacci(i));
  }
}
于 2009-10-08T12:39:04.270 に答える
6

アクションを使用して、実行中のデータベース操作をトランザクションに適切にカプセル化します。

public class InTran
{
    protected virtual string ConnString
    {
        get { return ConfigurationManager.AppSettings["YourDBConnString"]; }
    }

    public void Exec(Action<DBTransaction> a)
    {
        using (var dbTran = new DBTransaction(ConnString))
        {
            try
            {
                a(dbTran);
                dbTran.Commit();
            }
            catch
            {
                dbTran.Rollback();
                throw;
            }
        }
    }
}

今トランザクションで実行するために私は単にします

new InTran().Exec(tran => ...some SQL operation...);

InTranクラスは共通のライブラリに常駐できるため、重複が減り、将来の機能調整のための単一の場所が提供されます。

于 2009-10-08T17:21:00.823 に答える
6

同じ質問に2回答えるかどうかは悪い形式ですが、一般的にこれらのタイプをより適切に使用するためのアイデアを得るには、関数型プログラミングに関するJeremyMillerのMSDN記事を読むことをお勧めします。

日常の.NET開発のための関数型プログラミング

于 2009-10-08T20:55:51.310 に答える
2

実際、私はこれをstackoverflowで見つけました(少なくとも-アイデア):

public static T Get<T>  
    (string cacheKey, HttpContextBase context, Func<T> getItemCallback)
            where T : class
{
    T item = Get<T>(cacheKey, context);
    if (item == null) {
        item = getItemCallback();
        context.Cache.Insert(cacheKey, item);
    }

    return item;
}
于 2009-10-08T12:11:12.443 に答える
2

それらを汎用的に保ち、複数の引数をサポートすることで、同じことを行う強い型のデリゲートや冗長なデリゲートを作成する必要がなくなります。

于 2009-10-08T12:48:46.393 に答える
0

コンストラクターでジェネリックFuncまたはAction、およびいくつかのテキストを受け入れる別のフォームがあります。フォームにテキストを表示し、アニメーションを表示しながら、別のスレッドでFunc/Actionを実行します。

これは私の個人的なUtilライブラリにあり、中程度の長さの操作を実行して、邪魔にならない方法でUIをブロックしたいときはいつでも使用します。

フォームにプログレスバーを配置して、実行時間の長い操作を実行できるようにすることも検討しましたが、実際にはまだ必要ありません。

于 2009-10-08T15:20:32.687 に答える