Func <>とAction <>について私が見つけることができるすべての例はsimpleのようにhowそれらは技術的には機能しますが、以前は解決できなかった、またはより複雑な方法でしか解決できなかった問題を解決する例で使用したいと思います。つまり、どのように機能するかがわかり、簡潔で強力ななので、広義それらが解決する問題の種類と、アプリケーションの設計でそれらを使用する方法について理解したいと思います。
実際の問題を解決するために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();
}
}
}
また、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);
}
Linqを使用します。
List<int> list = { 1, 2, 3, 4 };
var even = list.Where(i => i % 2);
Where
のパラメーターはFunc<int, bool>
。
ラムダ式は、私のお気に入りのC#の1つです。 :)
常にAction
およびFunc
デリゲートを使用します。通常、スペースを節約するためにラムダ構文で宣言し、主に大きなメソッドのサイズを縮小するために使用します。メソッドを確認すると、似たようなコードセグメントが目立つことがあります。これらの場合、同様のコードセグメントをAction
またはFunc
にまとめます。デリゲートを使用すると、冗長なコードが削減され、コードセグメントにNice署名が付与され、必要に応じて簡単にメソッドに昇格できます。
以前は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);
}
私が使用しているものの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));
}
}
Dunnoは、同じ質問に2回答えるかどうかが悪い場合ですが、一般にこれらのタイプをより適切に使用するためのアイデアを得るために、Jeremy Millerの関数型プログラミングに関するMSDN記事を読むことをお勧めします。
アクションを使用して、実行中のデータベース操作をトランザクションにうまくカプセル化します。
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クラスは共通ライブラリに常駐でき、重複を減らし、将来の機能調整のために単一の場所を提供します。
それらを汎用的に保ち、複数の引数をサポートすることで、同じことを行う強力な型指定されたデリゲートまたは冗長なデリゲートを作成する必要がなくなります。
実際、私はこれを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;
}
コンストラクターで汎用FuncまたはActionとテキストを受け入れる別のフォームがあります。フォームにテキストを表示し、アニメーションを表示しながら、別のスレッドでFunc/Actionを実行します。
これは個人用のUtilライブラリにあり、中程度の長さの操作を行い、邪魔にならない方法でUIをブロックしたいときに使用します。
フォームにプログレスバーを配置することを検討しました。これにより、実行時間の長い操作を実行できるようになりましたが、まだ必要ありません。