c# - unity - 在設計應用程序時如何使用Func<>和Action<>?




where predicate c# (6)

所有關於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();

        }
    }
}

不知道如果它是不好的形式來回答相同的問題兩次或不是,但為了更好地使用這些類型一般我建議閱讀Jeremy Miller的關於函數式編程的MSDN文章:

每天.NET開發的函數式編程


使用linq。

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

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

Where是一個Func<int, bool>

Lambda表達式是我最喜歡的C#部分之一。 :)


它們也適用於重構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;
    }
}

使用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);
}

我一直使用ActionFunc代表。 我通常用lambda語法聲明它們以節省空間並主要用它們來減小大型方法的大小。 當我回顧我的方法時,有時類似的代碼段會脫穎而出。 在這些情況下,我將相似的代碼段封裝到ActionFunc 。 使用委託可以減少冗餘代碼,為代碼段提供一個很好的簽名,並且如果需要的話可以很容易地將其提升為方法。

我曾經寫過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);
}

我使用它的一個方面是緩存昂貴的方法調用,在給定相同的輸入時永遠不會改變:

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));
  }
}

我有一個單獨的表單,它在構造函數中接受一個通用的Func或Action以及一些文本。 它在單獨的線程上執行Func / Action,同時在窗體中顯示一些文本並顯示動畫。

它位於我的個人Util庫中,每當我想進行中等長度的操作並以非侵入方式阻止UI時,我都會使用它。

我考慮在表單上放置一個進度條,以便它可以執行更長時間的運行操作,但我還沒有真正需要它。





func