在devise应用程序时,如何使用Func <>和Action <>?
所有关于Func <>和Action <>的例子都很简单,就像下面的例子一样,你可以看到它们在技术上是如何工作的,但是我希望看到它们用在解决以前无法解决的问题的例子中只有以更复杂的方式来解决,即我知道它们是如何工作的,我可以看到它们是简洁而有力量的 ,所以我想更深入地理解它们解决什么样的问题,以及如何在应用程序devise。
你用什么方式(模式)使用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语句。
以下面(尽pipe简单)为例:
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; } }
通过一个Action委托,你可以重构它,如下所示:
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>
。
Lambdaexpression式是我最喜欢的C#部分之一。 🙂
我一直使用Action
和Func
代表。 我通常用lambda语法声明它们以节省空间,并主要用它们来减小大方法的大小。 当我回顾我的方法时,有时类似的代码段将脱颖而出。 在这些情况下,我将相似的代码段封装到Action
或Func
。 使用委托减less了冗余代码,给代码段提供了一个很好的签名,并且如果需要的话可以很容易地被提升为一个方法。
我曾经写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); }
我使用它的一个方面是caching昂贵的方法调用,永远不会改变给定相同的input:
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; }; }
默认的recursion斐波那契示例:
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)); } }
不知道,如果是不好的forms来回答相同的问题两次或不是,但为了更好地使用这些types一般我build议阅读杰里米尔的MSDN文章函数式编程:
每日.NET开发的函数式编程
我使用一个Action很好地封装了一个事务中正在执行的数据库操作:
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类可以驻留在一个共同的库中,减less重复,并为未来的function调整提供了一个单一的位置。
通过保持它们的通用性和支持多个参数,它使我们可以避免创build强types的委托或冗余委托来执行相同的操作。
其实,我发现这个在stackoverflow(至less – 这个想法):
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,同时在窗体中显示一些文本并显示animation。
这是在我个人的Util库中,每当我想做一个中等长度的操作,并以非侵入的方式阻止UI的时候,我就使用它。
我考虑在表单上放置一个进度条,这样它可以执行更长时间的运行操作,但我还没有真正需要它。