如何使用 Func<>和动作>>设计应用程序时?

发布于 2024-08-07 14:41:17 字数 1364 浏览 2 评论 0 原文

我能找到的关于 Func<> 的所有例子和动作>>很简单,如下所示,您可以在其中看到它们在技术上如何工作,但我希望看到它们在示例中使用,以解决以前无法解决或可以解决的问题只能以更复杂的方式解决,即我知道它们是如何工作的,并且我可以看到它们简洁而强大,所以我想从更大的意义上理解它们它们解决了哪些类型的问题以及我如何在应用程序设计中使用它们。

您以什么方式(模式)使用 Func<>和动作>>解决实际问题?

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

        }
    }
}

All the examples I can find about Func<> and Action<> are simple as in the one below where you see how they technically work but I would like to see them used in examples where they solve problems that previously could not be solved or could be solved only in a more complex way, i.e. I know how they work and I can see they are terse and powerful, so I want to understand them in a larger sense of what kinds of problems they solve and how I could use them in the design of applications.

In what ways (patterns) do you use Func<> and Action<> to solve real problems?

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

        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

一梦浮鱼 2024-08-14 14:41:17

它们对于重构 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);
}

They're also handy for refactoring switch statements.

Take the following (albeit simple) example:

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

With an Action delegate, you can refactor it as follows:

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);
}
情释 2024-08-14 14:41:17

使用 linq。

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

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

Where 的参数是 Func

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

Using linq.

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

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

The parameter for Where is an Func<int, bool>.

Lambda expressions are one of my favorite parts of C#. :)

暗恋未遂 2024-08-14 14:41:17

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

I use the Action and Func delegates all the time. I typically declare them with lambda syntax to save space and use them primarily to reduce the size of large methods. As I review my method, sometimes code segments that are similar will stand out. In those cases, I wrap up the similar code segments into Action or Func. Using the delegate reduces redundant code, give a nice signature to the code segment and can easily be promoted to a method if need be.

I used to write Delphi code and you could declare a function within a function. Action and Func accomplish this same behavior for me in c#.

Here's a sample of repositioning controls with a delegate:

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);
}
左秋 2024-08-14 14:41:17

我使用它的一件事是缓存昂贵的方法调用,这些方法调用在给定相同输入的情况下永远不会改变:

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

One thing I use it for is Caching of expensive method calls that never change given the same 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;
    };
}

The default recursive fibonacci example:

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));
  }
}
一个人的旅程 2024-08-14 14:41:17

不知道回答同一个问题两次是否是一种不好的形式,但为了获得一些更好地使用这些类型的想法,我建议阅读 Jeremy Miller 的关于函数式编程的 MSDN 文章:

日常 .NET 开发的函数式编程

Dunno if it's bad form to answer the same question twice or not, but to get some ideas for better uses of these types in general I suggest reading Jeremy Miller's MSDN article on Functional Programming:

Functional Programming for Everyday .NET Development

2024-08-14 14:41:17

我使用 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 类可以驻留在公共库中,从而减少重复并为将来的功能调整提供单一位置。

I use an Action to nicely encapsulate executing database operations in a transaction:

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

Now to execute in a transaction I simply do

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

The InTran class can reside in a common library, reducing duplication and provides a singe location for future functionality adjustments.

指尖上得阳光 2024-08-14 14:41:17

通过保持它们的通用性并支持多个参数,它使我们能够避免创建强类型委托或执行相同操作的冗余委托。

By keeping them generic and supporting multiple arguments, it allows us to avoid having to create strong typed delegates or redundant delegates that do the same thing.

后知后觉 2024-08-14 14:41:17

实际上,我在 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;
}

Actually, i found this at stackoverflow (at least - the idea):

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;
}
戴着白色围巾的女孩 2024-08-14 14:41:17

我有一个单独的表单,它接受构造函数中的通用 Func 或 Action 以及一些文本。它在单独的线程上执行 Func/Action,同时在表单中显示一些文本并显示动画。

它位于我的个人 Util 库中,每当我想要执行中等长度的操作并以非侵入式方式阻止 UI 时,我都会使用它。

我考虑过在表单上放置一个进度条,以便它可以执行更长时间的运行操作,但我还没有真正需要它。

I have a separate form that accepts a generic Func or an Action in the constructor as well as some text. It executes the Func/Action on a separate thread while displaying some text in the form and showing an animation.

It's in my personal Util library, and I use it whenever I want to do a medium length operation and block the UI in a non-intrusive way.

I considered putting a progress bar on the form as well, so that it could perform longer running operations but I haven't really needed it to yet.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文