目 录CONTENT

文章目录

CSharp(四十四) 内置委托 Action 和 Func 的定义、使用和注意事项

C# 中内置委托 Action 和 Func 的定义、使用和注意事项

一、先理解:什么是委托

在 C# 中,委托可以理解为:

一种可以保存方法的变量类型。

平时变量可以保存数字、字符串、对象:

int age = 18;
string name = "小明";

委托则可以保存“方法”:

Action sayHello = () =>
{
    Console.WriteLine("你好");
};

sayHello();

这里的 sayHello 不是普通数据,而是保存了一段可以执行的代码。

可以这样理解:

普通变量保存数据,委托变量保存方法。


二、为什么需要内置委托

在没有 ActionFunc 之前,如果我们想定义一个委托,通常要自己写:

public delegate void MyDelegate();

然后再使用:

MyDelegate method = () =>
{
    Console.WriteLine("执行方法");
};

method();

但是很多委托的形状都很常见,比如:

  • 没有参数,没有返回值
  • 有一个参数,没有返回值
  • 有两个参数,没有返回值
  • 没有参数,有返回值
  • 有一个参数,有返回值
  • 有多个参数,有返回值

如果每次都自己定义委托,就会很麻烦。

所以 C# 提供了常用的内置委托:

  • Action
  • Func

它们可以覆盖大多数常见场景。

一句话:

ActionFunc 是 C# 已经帮我们定义好的通用委托类型。


三、Action 是什么

Action 表示:

没有返回值的方法。

也就是说,只要一个方法的返回类型是 void,就有机会用 Action 表示。

1. 没有参数、没有返回值

Action sayHello = () =>
{
    Console.WriteLine("你好,C#");
};

sayHello();

这里:

  • Action 表示一个没有参数、没有返回值的方法。
  • sayHello 是委托变量。
  • () => { ... } 是匿名函数。
  • sayHello() 表示调用这个委托。

也可以写成普通方法:

static void SayHello()
{
    Console.WriteLine("你好,C#");
}

Action action = SayHello;
action();

2. 一个参数、没有返回值

如果方法有一个参数,可以使用 Action<T>

Action<string> printName = name =>
{
    Console.WriteLine("姓名:" + name);
};

printName("小明");

这里:

Action<string>

表示:

这个委托保存的方法需要一个 string 参数,并且没有返回值。

等价于自己定义:

public delegate void MyAction(string value);

3. 多个参数、没有返回值

如果方法有多个参数,可以继续写多个泛型类型。

Action<string, int> printInfo = (name, age) =>
{
    Console.WriteLine($"{name} 今年 {age} 岁");
};

printInfo("小明", 18);

这里:

Action<string, int>

表示:

这个委托保存的方法有两个参数,第一个是 string,第二个是 int,没有返回值。

再比如:

Action<string, int, double> printScore = (name, age, score) =>
{
    Console.WriteLine($"{name},年龄 {age},成绩 {score}");
};

printScore("小红", 17, 95.5);

四、Func 是什么

Func 表示:

有返回值的方法。

它和 Action 最大的区别是:

  • Action 没有返回值。
  • Func 一定有返回值。

1. 没有参数、有返回值

Func<int> getNumber = () =>
{
    return 100;
};

int result = getNumber();

Console.WriteLine(result); // 输出 100

这里:

Func<int>

表示:

没有参数,返回一个 int

如果函数体只有一个表达式,可以简写:

Func<int> getNumber = () => 100;

2. 一个参数、有返回值

Func<int, int> square = x =>
{
    return x * x;
};

Console.WriteLine(square(5)); // 输出 25

这里:

Func<int, int>

表示:

第一个 int 是参数类型,第二个 int 是返回值类型。

也就是:

Func<参数类型, 返回值类型>

可以简写:

Func<int, int> square = x => x * x;

3. 多个参数、有返回值

Func<int, int, int> add = (a, b) =>
{
    return a + b;
};

Console.WriteLine(add(3, 5)); // 输出 8

这里:

Func<int, int, int>

前两个 int 是参数类型,最后一个 int 是返回值类型。

也就是:

Func<参数1类型, 参数2类型, 返回值类型>

再看一个例子:

Func<string, int, string> buildMessage = (name, age) =>
{
    return $"{name} 今年 {age} 岁";
};

Console.WriteLine(buildMessage("小明", 18));

这里:

Func<string, int, string>

表示:

参数是 stringint,返回值是 string


五、Action 和 Func 的核心区别

对比项 Action Func
是否有返回值 没有返回值 必须有返回值
返回类型 void 最后一个泛型参数
常见形式 Action<T> Func<T, TResult>
适合场景 执行动作 计算结果

简单记忆:

只做事,不返回结果,用 Action
做完事,还要返回结果,用 Func

例子:

Action<string> print = text => Console.WriteLine(text);

这个只是打印,不返回结果,所以用 Action

Func<int, int, int> add = (a, b) => a + b;

这个要返回计算结果,所以用 Func


六、Action 的常见写法

1. 保存普通方法

static void PrintHello()
{
    Console.WriteLine("Hello");
}

Action action = PrintHello;

action();

2. 保存匿名函数

Action action = delegate
{
    Console.WriteLine("Hello");
};

action();

这是较早的匿名方法写法。

3. 保存 Lambda 表达式

Action action = () =>
{
    Console.WriteLine("Hello");
};

action();

如果只有一行代码,可以简写:

Action action = () => Console.WriteLine("Hello");

4. 带参数的 Action

Action<string> print = text => Console.WriteLine(text);

print("你好");

多个参数:

Action<string, int> printUser = (name, age) =>
{
    Console.WriteLine($"{name}:{age}");
};

printUser("小明", 18);

七、Func 的常见写法

1. 保存普通方法

static int Add(int a, int b)
{
    return a + b;
}

Func<int, int, int> add = Add;

Console.WriteLine(add(10, 20));

2. 保存 Lambda 表达式

Func<int, int, int> add = (a, b) =>
{
    return a + b;
};

Console.WriteLine(add(10, 20));

简写:

Func<int, int, int> add = (a, b) => a + b;

3. 返回字符串

Func<string, string> greet = name => "你好," + name;

Console.WriteLine(greet("小明"));

4. 返回布尔值

Func<int, bool> isAdult = age => age >= 18;

Console.WriteLine(isAdult(20)); // True
Console.WriteLine(isAdult(16)); // False

这种返回 boolFunc 很常见,常用于判断条件。


八、Action 和 Func 的调用方式

委托变量可以像方法一样调用。

Action sayHello = () => Console.WriteLine("Hello");

sayHello();

也可以使用 Invoke

sayHello.Invoke();

带参数:

Action<string> print = text => Console.WriteLine(text);

print("你好");
print.Invoke("你好");

有返回值:

Func<int, int, int> add = (a, b) => a + b;

int result1 = add(1, 2);
int result2 = add.Invoke(1, 2);

常见写法更推荐直接像方法一样调用:

add(1, 2);

九、使用场景一:作为方法参数

ActionFunc 最常见的用途之一,就是把一段逻辑传给另一个方法。

1. 把 Action 当作参数

static void DoWork(Action callback)
{
    Console.WriteLine("开始工作");

    callback();

    Console.WriteLine("工作结束");
}

调用:

DoWork(() =>
{
    Console.WriteLine("执行传进来的操作");
});

输出:

开始工作
执行传进来的操作
工作结束

这里的 callback 可以理解为“回调函数”:

我先把一段代码交给你,你在合适的时候帮我执行。

2. 把 Func 当作参数

static int Calculate(int a, int b, Func<int, int, int> operation)
{
    return operation(a, b);
}

调用:

int sum = Calculate(10, 5, (x, y) => x + y);
int difference = Calculate(10, 5, (x, y) => x - y);
int product = Calculate(10, 5, (x, y) => x * y);

Console.WriteLine(sum);        // 15
Console.WriteLine(difference); // 5
Console.WriteLine(product);    // 50

这样 Calculate 方法就不需要固定写死加法、减法或乘法,而是由外部传入计算规则。


十、使用场景二:简化重复代码

假设有三段代码都需要记录开始和结束:

Console.WriteLine("开始");
// 做事情 A
Console.WriteLine("结束");

Console.WriteLine("开始");
// 做事情 B
Console.WriteLine("结束");

可以用 Action 抽取公共流程:

static void RunWithLog(Action action)
{
    Console.WriteLine("开始");
    action();
    Console.WriteLine("结束");
}

使用:

RunWithLog(() =>
{
    Console.WriteLine("保存用户");
});

RunWithLog(() =>
{
    Console.WriteLine("发送邮件");
});

如果任务有返回值,可以用 Func

static T RunWithLog<T>(Func<T> func)
{
    Console.WriteLine("开始");
    T result = func();
    Console.WriteLine("结束");

    return result;
}

使用:

int result = RunWithLog(() =>
{
    return 100 + 200;
});

Console.WriteLine(result);

十一、使用场景三:LINQ 中的 Func

LINQ 中大量使用 Func

例如:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

var evenNumbers = numbers.Where(n => n % 2 == 0);

这里的:

n => n % 2 == 0

本质上可以理解为一个:

Func<int, bool>

也就是:

输入一个 int,返回一个 bool,用来判断这个数字是否满足条件。

再比如:

var squares = numbers.Select(n => n * n);

这里的:

n => n * n

可以理解为:

Func<int, int>

表示:

输入一个 int,返回一个新的 int

对象列表示例:

class Student
{
    public string Name { get; set; }
    public int Score { get; set; }
}
List<Student> students = new List<Student>
{
    new Student { Name = "小明", Score = 95 },
    new Student { Name = "小红", Score = 80 },
    new Student { Name = "小刚", Score = 59 }
};

var passedStudents = students.Where(s => s.Score >= 60);
var names = students.Select(s => s.Name);

这里:

  • s => s.Score >= 60 是判断条件。
  • s => s.Name 是转换规则。

十二、使用场景四:事件和回调

简单事件可以使用 Action

class Button
{
    public event Action Click;

    public void Press()
    {
        Console.WriteLine("按钮被按下");
        Click?.Invoke();
    }
}

使用:

Button button = new Button();

button.Click += () =>
{
    Console.WriteLine("处理点击事件");
};

button.Press();

带参数事件:

class Downloader
{
    public event Action<int> ProgressChanged;

    public void Download()
    {
        for (int progress = 0; progress <= 100; progress += 25)
        {
            ProgressChanged?.Invoke(progress);
        }
    }
}

使用:

Downloader downloader = new Downloader();

downloader.ProgressChanged += progress =>
{
    Console.WriteLine($"当前进度:{progress}%");
};

downloader.Download();

注意:

公开给外部使用的标准事件,实际项目中通常更推荐 EventHandlerEventHandler<TEventArgs>
Action 更适合简单、内部、教学或轻量场景。


十三、使用场景五:策略传入

有时候,我们希望一个方法的行为可以由外部决定。

例如筛选数字:

static List<int> Filter(List<int> numbers, Func<int, bool> condition)
{
    List<int> result = new List<int>();

    foreach (int number in numbers)
    {
        if (condition(number))
        {
            result.Add(number);
        }
    }

    return result;
}

使用:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

List<int> evenNumbers = Filter(numbers, n => n % 2 == 0);
List<int> bigNumbers = Filter(numbers, n => n > 3);

这里的 condition 就是外部传进来的判断策略。

如果不用 Func,我们可能要为每一种筛选规则都写一个单独的方法。

使用 Func 后,一个 Filter 方法就能支持很多规则。


十四、使用场景六:延迟执行

Func 可以把“现在不执行,等需要的时候再执行”的逻辑保存下来。

Func<int> getRandomNumber = () =>
{
    Console.WriteLine("正在生成随机数");
    return new Random().Next(1, 100);
};

这段代码只是把逻辑保存到 getRandomNumber 中,还没有真正生成随机数。

只有调用时才会执行:

int number = getRandomNumber();

这叫延迟执行。

简单理解:

Func 保存的是一张“以后再执行”的任务卡。


十五、使用场景七:异步方法

异步方法也可以用 FuncAction 表示,但要注意返回类型。

1. 异步无返回结果:Func

Func<Task> loadDataAsync = async () =>
{
    await Task.Delay(1000);
    Console.WriteLine("数据加载完成");
};

await loadDataAsync();

虽然这个异步方法没有返回业务数据,但它返回的是 Task,所以应该用:

Func<Task>

而不是:

Action

2. 异步有返回结果:Func<Task>

Func<Task<int>> getNumberAsync = async () =>
{
    await Task.Delay(1000);
    return 100;
};

int number = await getNumberAsync();

带参数:

Func<string, Task<string>> downloadAsync = async url =>
{
    await Task.Delay(1000);
    return "下载完成:" + url;
};

string result = await downloadAsync("https://example.com");

注意:

异步逻辑通常用 Func<Task>Func<Task<T>>,不要随便用 async voidAction


十六、Action 和 Func 的泛型参数顺序

这是初学者最容易混淆的地方。

Action 的泛型参数都是参数类型

Action<string, int, double>

表示:

void 方法名(string value1, int value2, double value3)

它没有返回值。

Func 的最后一个泛型参数是返回值类型

Func<string, int, double>

表示:

double 方法名(string value1, int value2)

注意:

  • 前面的 stringint 是参数类型。
  • 最后的 double 是返回值类型。

再看几个例子:

Func<int>

表示:

int 方法名()
Func<int, string>

表示:

string 方法名(int value)
Func<int, int, string>

表示:

string 方法名(int value1, int value2)

记忆口诀:

Action 全是参数。
Func 最后一个是返回值。


十七、Action 和 Func 可以有多少个参数

在 .NET 中,常见的 ActionFunc 泛型委托支持多个参数。

常见形式:

Action
Action<T1>
Action<T1, T2>
Action<T1, T2, T3>
Func<TResult>
Func<T1, TResult>
Func<T1, T2, TResult>
Func<T1, T2, T3, TResult>

实际框架中通常最多支持到 16 个输入参数。

不过教学和实际开发中要提醒学生:

如果一个委托需要很多参数,往往说明设计可以优化。

比如下面这种写法可读性很差:

Action<string, int, double, bool, DateTime> action;

更推荐把相关参数封装成一个类:

class UserInfo
{
    public string Name { get; set; }
    public int Age { get; set; }
    public double Score { get; set; }
    public bool IsActive { get; set; }
    public DateTime CreatedTime { get; set; }
}

然后:

Action<UserInfo> action;

这样代码更容易理解和维护。


十八、注意事项一:返回值类型必须匹配

Func<int, int> square = x => x * x;

这里要求返回 int

下面这样是错误的:

// 错误:要求返回 int,但实际返回 string
Func<int, int> wrong = x => "结果是:" + x;

应该改成:

Func<int, string> right = x => "结果是:" + x;

同理,参数类型也必须匹配:

Action<int> print = number => Console.WriteLine(number);

print(100);

不能传字符串:

// 错误
print("100");

十九、注意事项二:Action 不能返回值

错误写法:

// 错误:Action 没有返回值
Action<int> square = x => x * x;

因为:

x => x * x

会产生一个结果。

如果要返回结果,应该使用 Func

Func<int, int> square = x => x * x;

如果真的只是执行动作,不需要返回值,可以写:

Action<int> printSquare = x =>
{
    Console.WriteLine(x * x);
};

二十、注意事项三:Func 必须返回值

错误写法:

// 错误:Func<int> 要求返回 int
Func<int> getNumber = () =>
{
    Console.WriteLine("没有返回数字");
};

正确写法:

Func<int> getNumber = () =>
{
    Console.WriteLine("返回数字");
    return 100;
};

如果不需要返回值,就使用 Action

Action print = () =>
{
    Console.WriteLine("不需要返回值");
};

二十一、注意事项四:语句 Lambda 要写 return

表达式 Lambda 可以自动返回结果:

Func<int, int> square = x => x * x;

语句 Lambda 使用 {} 后,需要显式写 return

Func<int, int> square = x =>
{
    return x * x;
};

错误写法:

// 错误
Func<int, int> square = x =>
{
    x * x;
};

只要写了 {},就不是简单表达式了,返回值要用 return 明确写出来。


二十二、注意事项五:调用前注意是否为 null

委托变量可能没有赋值。

Action action = null;

action(); // 会报 NullReferenceException

更安全的写法:

action?.Invoke();

带参数:

Action<string> print = null;

print?.Invoke("你好");

有返回值时要注意默认值:

Func<int> getNumber = null;

int result = getNumber?.Invoke() ?? 0;

这里:

  • 如果 getNumber 不为 null,就调用它。
  • 如果 getNumbernull,就使用默认值 0

二十三、注意事项六:闭包问题

ActionFunc 经常配合 Lambda 使用,而 Lambda 可以访问外部变量。

这种现象叫闭包。

int count = 0;

Action increase = () =>
{
    count++;
    Console.WriteLine(count);
};

increase(); // 1
increase(); // 2
increase(); // 3

这很方便,但也可能带来意外。

int number = 10;

Func<int> getNumber = () => number;

number = 20;

Console.WriteLine(getNumber()); // 输出 20

很多初学者以为会输出 10,但实际输出 20。

原因是:

Lambda 捕获的是变量本身,不只是当时的值。

如果想固定当时的值,可以复制一份:

int number = 10;
int snapshot = number;

Func<int> getNumber = () => snapshot;

number = 20;

Console.WriteLine(getNumber()); // 输出 10

二十四、注意事项七:循环中的闭包

看下面的例子:

List<Action> actions = new List<Action>();

for (int i = 0; i < 3; i++)
{
    actions.Add(() => Console.WriteLine(i));
}

foreach (Action action in actions)
{
    action();
}

很多初学者会以为输出:

0
1
2

但在循环变量捕获的场景中,实际结果可能不是自己预期的。

更稳妥的写法是在循环内部复制一份:

List<Action> actions = new List<Action>();

for (int i = 0; i < 3; i++)
{
    int current = i;

    actions.Add(() => Console.WriteLine(current));
}

foreach (Action action in actions)
{
    action();
}

这样每个 Lambda 捕获的是各自的 current

教学时可以这样说:

循环变量像同一个盒子反复换数字,复制一份就是给每次循环准备一个新盒子。


二十五、注意事项八:不要让委托过于复杂

下面这种代码虽然能写,但不适合教学和维护:

Func<Student, bool> condition = s =>
    s.Score > 60
    && s.Name.StartsWith("小")
    && s.Name.Length > 1
    && s.Score + s.ExtraScore > 90;

如果逻辑变复杂,建议提取成普通方法:

static bool IsExcellentStudent(Student student)
{
    return student.Score > 60
        && student.Name.StartsWith("小")
        && student.Name.Length > 1
        && student.Score + student.ExtraScore > 90;
}

然后:

Func<Student, bool> condition = IsExcellentStudent;

原则:

短逻辑可以用 Lambda,复杂逻辑应该提取成有名字的方法。


二十六、注意事项九:异步时不要用 Action 表示 async 逻辑

不推荐:

Action action = async () =>
{
    await Task.Delay(1000);
    Console.WriteLine("完成");
};

这类写法容易变成类似 async void 的效果,异常处理和等待流程都不够清晰。

更推荐:

Func<Task> actionAsync = async () =>
{
    await Task.Delay(1000);
    Console.WriteLine("完成");
};

await actionAsync();

如果有返回值:

Func<Task<int>> getNumberAsync = async () =>
{
    await Task.Delay(1000);
    return 100;
};

int number = await getNumberAsync();

记住:

异步无结果用 Func<Task>,异步有结果用 Func<Task<T>>


二十七、注意事项十:多播委托的返回值问题

委托可以同时保存多个方法,这叫多播委托。

Action 比较容易理解:

Action action = () => Console.WriteLine("第一个");
action += () => Console.WriteLine("第二个");

action();

输出:

第一个
第二个

但是 Func 有返回值,如果多个方法都返回结果,会发生什么?

Func<int> func = () =>
{
    Console.WriteLine("第一个");
    return 1;
};

func += () =>
{
    Console.WriteLine("第二个");
    return 2;
};

int result = func();

Console.WriteLine(result);

输出类似:

第一个
第二个
2

多个方法都会执行,但最终拿到的通常是最后一个方法的返回值。

所以:

多播委托更适合 Action
对于 Func,如果关心每个返回值,不建议直接多播。


二十八、Action 和 Func 与自定义委托的选择

既然有了 ActionFunc,是不是就不需要自定义委托了?

不是。

ActionFunc 适合通用场景。

例如:

Func<int, int, int> operation

它能表达“两个整数输入,一个整数输出”,但看不出业务含义。

有时自定义委托更清楚:

public delegate decimal DiscountCalculator(decimal price);

这个名字直接告诉我们:

这是一个折扣计算器。

对比:

写法 优点 缺点
Action / Func 简洁、通用、少写代码 业务含义不够明显
自定义委托 名字能表达业务意义 多写一段定义

建议:

  • 简单逻辑、局部使用,用 Action / Func
  • 公开 API、复杂业务、需要表达明确含义时,可以自定义委托。

二十九、Action 和 Func 与 Predicate 的关系

C# 里还有一个常见委托:

Predicate<T>

它表示:

bool 方法名(T value)

例如:

Predicate<int> isEven = n => n % 2 == 0;

它和下面写法很像:

Func<int, bool> isEven = n => n % 2 == 0;

都表示输入一个 int,返回一个 bool

区别是:

  • Predicate<T> 更强调“判断条件”。
  • Func<T, bool> 更通用。

初学时重点掌握 ActionFunc 即可。


三十、完整示例:使用 Action 和 Func 制作小型计算器

using System;

class Program
{
    static void Main()
    {
        Action<string> printTitle = title =>
        {
            Console.WriteLine("==== " + title + " ====");
        };

        Func<int, int, int> add = (a, b) => a + b;
        Func<int, int, int> subtract = (a, b) => a - b;
        Func<int, int, int> multiply = (a, b) => a * b;

        printTitle("计算器");

        PrintResult("加法", 10, 5, add);
        PrintResult("减法", 10, 5, subtract);
        PrintResult("乘法", 10, 5, multiply);
    }

    static void PrintResult(
        string operationName,
        int a,
        int b,
        Func<int, int, int> operation)
    {
        int result = operation(a, b);

        Console.WriteLine($"{operationName}:{a} 和 {b} 的结果是 {result}");
    }
}

输出:

==== 计算器 ====
加法:10 和 5 的结果是 15
减法:10 和 5 的结果是 5
乘法:10 和 5 的结果是 50

这个例子中:

  • Action<string> 用来打印标题。
  • Func<int, int, int> 用来表示不同计算规则。
  • PrintResult 方法不关心具体怎么算,只负责调用传进来的计算逻辑。

三十一、完整示例:使用 Func 筛选学生

using System;
using System.Collections.Generic;

class Student
{
    public string Name { get; set; }
    public int Score { get; set; }
}

class Program
{
    static void Main()
    {
        List<Student> students = new List<Student>
        {
            new Student { Name = "小明", Score = 95 },
            new Student { Name = "小红", Score = 82 },
            new Student { Name = "小刚", Score = 59 },
            new Student { Name = "小丽", Score = 76 }
        };

        List<Student> passedStudents = Filter(students, s => s.Score >= 60);
        List<Student> excellentStudents = Filter(students, s => s.Score >= 90);

        Console.WriteLine("及格学生:");
        PrintStudents(passedStudents);

        Console.WriteLine("优秀学生:");
        PrintStudents(excellentStudents);
    }

    static List<Student> Filter(List<Student> students, Func<Student, bool> condition)
    {
        List<Student> result = new List<Student>();

        foreach (Student student in students)
        {
            if (condition(student))
            {
                result.Add(student);
            }
        }

        return result;
    }

    static void PrintStudents(List<Student> students)
    {
        foreach (Student student in students)
        {
            Console.WriteLine($"{student.Name}:{student.Score}");
        }
    }
}

这里的核心是:

Func<Student, bool> condition

它表示:

传入一个学生,返回这个学生是否满足条件。

所以我们可以传入不同规则:

s => s.Score >= 60
s => s.Score >= 90

同一个 Filter 方法就能完成不同筛选任务。


三十二、课堂讲解建议

ActionFunc 时,可以按照这个顺序:

  1. 先讲委托是“保存方法的变量”。
  2. 再讲为什么需要内置委托。
  3. Action:没有返回值。
  4. Func:有返回值,最后一个泛型参数是返回值。
  5. 用 Lambda 写短示例。
  6. 用方法参数讲回调。
  7. 用 LINQ 讲实际应用。
  8. 最后讲闭包、异步和返回值匹配这些注意事项。

学生最容易记混的是:

Func<int, string>

这不是返回 int,而是:

参数是 int,返回值是 string

可以反复强调:

Func 最后一个类型永远是返回值类型。


三十三、练习题

练习 1:使用 Action 打印欢迎语

定义一个 Action<string>,传入姓名并打印欢迎语。

参考答案:

Action<string> welcome = name =>
{
    Console.WriteLine("欢迎你," + name);
};

welcome("小明");

练习 2:使用 Func 计算平方

定义一个 Func<int, int>,传入一个整数,返回它的平方。

参考答案:

Func<int, int> square = x => x * x;

Console.WriteLine(square(6)); // 36

练习 3:使用 Func 判断偶数

定义一个 Func<int, bool>,判断一个数字是否是偶数。

参考答案:

Func<int, bool> isEven = n => n % 2 == 0;

Console.WriteLine(isEven(10)); // True
Console.WriteLine(isEven(11)); // False

练习 4:把计算规则作为参数

定义一个方法,接收两个整数和一个 Func<int, int, int>,根据传入的规则计算结果。

参考答案:

static int Calculate(int a, int b, Func<int, int, int> operation)
{
    return operation(a, b);
}

使用:

int result1 = Calculate(10, 5, (x, y) => x + y);
int result2 = Calculate(10, 5, (x, y) => x * y);

练习 5:使用 Func 筛选字符串

从字符串列表中筛选出长度大于 3 的字符串。

参考答案:

List<string> words = new List<string> { "C#", "Java", "Python", "Go" };

Func<string, bool> isLongWord = word => word.Length > 3;

foreach (string word in words)
{
    if (isLongWord(word))
    {
        Console.WriteLine(word);
    }
}

输出:

Java
Python

三十四、总结

ActionFunc 是 C# 中非常常用的内置委托,可以让我们更方便地保存方法、传递方法和执行回调。

可以记住下面几句话:

  1. 委托是可以保存方法的类型。
  2. Action 表示没有返回值的方法。
  3. Func 表示有返回值的方法。
  4. Action<T> 中的泛型参数都是方法参数类型。
  5. Func<T, TResult> 中最后一个泛型参数是返回值类型。
  6. 委托变量可以像普通方法一样调用。
  7. Action 常用于执行动作、回调、事件。
  8. Func 常用于计算结果、判断条件、LINQ 查询。
  9. 短逻辑适合 Lambda,复杂逻辑建议提取成普通方法。
  10. 异步逻辑更适合用 Func<Task>Func<Task<T>>

一句话概括:

ActionFunc 让方法可以像数据一样被保存、传递和调用,是理解 Lambda、事件、LINQ 和回调的重要基础。

0
博主关闭了当前页面的评论