1. 委托怎么定义?委托类型怎么声明?
委托是C#中的引用类型 ,本质是封装方法的类型 ,核心作用是把方法当作参数传递、实现回调逻辑、解耦方法调用方和执行方;
委托类型的声明必须用 delegate 关键字修饰,语法上需要明确返回值类型 和参数列表 (即方法签名),支持无参无返回、有参有返回、带ref/out修饰符的参数等形式,声明后就成为自定义的委托类型,可在项目中复用。
核心语法:
csharp
// 访问修饰符 + delegate + 返回值 + 委托类型名 + (参数列表);
[访问修饰符] delegate <返回值类型> 委托类型名([参数列表]);
示例:
csharp
// 无参、无返回值的委托类型(最常用)
internal delegate void HelloWorldDelegate();
// 有参、无返回值的委托类型
public delegate void ShowMsgDelegate(string msg);
// 有参、有返回值的委托类型
public delegate int CalculateDelegate(int a, int b);
2. 委托类型变量怎么声明?
首先需要提前声明好对应的委托类型,委托类型是自定义的合法类型 ,和C#内置的int、string,以及自定义类Person、Student的类型特性完全一致;
声明委托变量的语法和声明普通类型变量完全相同,委托类型 + 变量名 即可,委托变量的默认值为null(引用类型特性),调用前必须判空避免空引用异常。
核心语法:
csharp
// 先声明委托类型
delegate void HelloWorldDelegate();
// 再声明委托变量
委托类型名 委托变量名;
示例:
csharp
// 声明委托变量,默认值为null
HelloWorldDelegate obj;
// 声明时可直接赋默认值null(显式写法,面试/开发更规范)
HelloWorldDelegate obj2 = null;
3. 声明委托类型变量后,怎么赋值?有哪几种方式?
给委托变量赋值的核心前提 :被赋值的方法签名(返回值+参数类型/个数/修饰符)必须和委托类型完全一致 (面试高频考点),静态方法、实例方法均可赋值。
主要有4种赋值方式 ,其中方法组隐式转换 和Lambda表达式是实际开发+面试中最常用的,另外两种是基础写法,Lambda更是C#3.0+的主流简化写法,必须掌握。
方式1:显式new委托实例(基础写法)
通过new关键字创建委托实例,括号内传入匹配签名的方法名,是最原始的赋值方式,语义清晰。
csharp
// 1. 定义委托类型
delegate void HelloWorldDelegate();
// 2. 定义匹配签名的方法
static void Show() => Console.WriteLine("执行Show方法");
// 3. 显式new赋值
HelloWorldDelegate obj = new HelloWorldDelegate(Show);
方式2:方法组隐式转换(常用简洁写法)
直接将方法名赋值给委托变量,C#编译器会自动将方法组转换为对应的委托实例,等价于方式1,开发中优先使用。
csharp
HelloWorldDelegate obj = Show; // 编译器自动转换,简化写法
方式3:给委托赋值实例方法(非静态方法)
委托不仅能绑定静态方法,还能绑定类的实例方法,此时委托实例的Target属性会指向该类的实例对象 (面试考点);若绑定静态方法,Target为null。
csharp
// 定义类和实例方法
public class Program
{
public void InstanceMethod() => Console.WriteLine("执行实例方法");
}
// 赋值实例方法
var p = new Program();
HelloWorldDelegate instanceDelegate = p.InstanceMethod;
// 输出:实例方法赋给委托时,target=Program
Console.WriteLine("实例方法赋给委托时,target=" + instanceDelegate.Target);
// 赋值静态方法,Target为null
HelloWorldDelegate staticDelegate = Show;
Console.WriteLine("静态方法赋给委托时,target=" + staticDelegate.Target); // 输出:null
方式4:Lambda表达式赋值(C#3.0+主流写法)
无需单独定义方法,直接通过Lambda表达式封装方法逻辑,简化委托的使用,尤其适合简单逻辑的回调,面试中高频考察。
csharp
// 无参无返回的Lambda赋值
HelloWorldDelegate obj = () => Console.WriteLine("Lambda实现委托逻辑");
// 有参有返回的Lambda赋值
CalculateDelegate cal = (a, b) => a + b;
4. 委托变量调用?
Invoke()是委托的默认方法,可简写:obj.Invoke() 等价于 obj();
多播委托是什么?委托相比直接调用函数的核心优势是什么?
多播委托定义
C#中的委托天生支持多播 ,一个委托实例可以封装多个匹配签名的方法 ,形成委托链 ,这种能执行多个方法的委托就是多播委托 ,底层基于System.Delegate类的委托链实现。
多播委托的使用
- 用
+=向委托链添加方法 ,用-=从委托链移除方法; - 调用时(
Invoke()/直接变量名),会按添加顺序依次执行委托链中的所有方法; - 移除方法时,若方法不在委托链中,不会抛出异常;
- 调用前必须判空(
obj?.Invoke()),避免委托链为null时的空引用异常。
示例:
csharp
delegate void HelloWorldDelegate();
static void Show() => Console.WriteLine("执行Show");
static void Print() => Console.WriteLine("执行Print");
// 初始化委托链
HelloWorldDelegate obj = Show;
// 添加方法到委托链
obj += Print;
// 调用多播委托,依次执行Show、Print
obj?.Invoke();
// 移除方法,委托链只剩Show
obj -= Print;
obj?.Invoke(); // 仅执行Show
5. 委托相比直接调用函数的核心优势(面试必答)
委托是对"方法调用"的抽象,解决了直接调用函数的耦合性高、灵活性差问题,核心优势有5点:
- 支持多播执行:一个委托调用可执行多个方法,无需手动逐个调用,简化批量方法执行逻辑;
- 实现方法解耦:调用方无需知道具体要执行哪个/哪些方法,只需和委托交互,符合"开闭原则";
- 方法作为参数传递 :可将方法当作参数传入其他方法,实现回调逻辑(如异步回调、集合遍历回调);
- 动态管理方法 :通过
+=/-=运行时动态添加/移除执行方法,无需修改代码逻辑; - 统一调用入口:无论封装的是静态方法、实例方法、Lambda,都通过委托的统一方式调用,简化代码结构。
6. 委托变量Invoke()调用后有返回值吗?多播委托的返回值规则是什么?
委托调用的返回值由委托类型的返回值决定 ,分单播委托 (委托链只有一个方法)和多播委托 (委托链有多个方法)两种情况,其中多播委托的返回值规则是面试高频考点。
1. 单播委托(单个方法)
调用Invoke()(或直接变量名)会返回对应方法的实际返回值,和直接调用方法的结果一致。
csharp
delegate int CalculateDelegate(int a, int b);
static int Add(int a, int b) => a + b;
CalculateDelegate cal = Add;
int result = cal.Invoke(1,2);
Console.WriteLine(result); // 输出:3
2. 多播委托(多个方法)
调用后只会返回委托链中最后一个被执行方法的返回值 ,前面所有方法的返回值都会被直接丢弃,无法获取。
正因为这个规则,多播委托通常设计为void返回值类型 (面试必记),如果需要获取多播中每个方法的返回值,需通过GetInvocationList()遍历委托链,逐个调用。
示例:多播委托的返回值规则 + 遍历获取所有返回值
csharp
delegate int CalculateDelegate(int a, int b);
static int Add(int a, int b) { Console.WriteLine("Add:" + (a+b)); return a+b; }
static int Multiply(int a, int b) { Console.WriteLine("Multiply:" + (a*b)); return a*b; }
// 多播委托:先Add,后Multiply
CalculateDelegate cal = Add;
cal += Multiply;
// 调用多播,仅返回最后一个方法Multiply的结果
int lastResult = cal.Invoke(2,3);
Console.WriteLine("多播返回值:" + lastResult); // 输出:6
// 遍历委托链,获取每个方法的返回值(面试拓展考点)
Delegate[] delegates = cal.GetInvocationList();
foreach (CalculateDelegate d in delegates)
{
int res = d.Invoke(2,3);
Console.WriteLine("遍历返回值:" + res); // 依次输出6、6?不,依次输出5、6
}
7. 多播与异常? 注意事项?
- 移除方法时,若方法不在委托链中,不会抛出异常;
- 调用前必须判空(
obj?.Invoke()),避免委托链为null时的空引用异常。 - 多播委托中,若某个方法抛出异常,委托链中后续方法会停止执行;
8. 举几个生活中的例子说明委托?委托解决的核心技术问题是什么?
生活中的委托例子(贴合委托核心逻辑,面试口语化回答即可)
委托的生活逻辑:自己不亲自做某件事,委托给符合"规则"的其他人/机构做,可委托一个,也可委托多个,还能随时更换被委托人,核心是「定规则、找执行者、解耦自己和执行者」,3个易懂例子:
- 公司招聘:比如公司招 Java 开发,先定好岗位要求这个统一规则,这就是委托的签名;我们公司和另一家公司都按这个规则找同一个猎头,就是不同的委托变量绑定同一个方法;如果我们公司同时找两个猎头,就是多播委托,一次委托两个猎头都帮我们找人。而委托解决的核心问题,就是把公司和候选人解耦了,我们不用自己找候选人,只对接猎头就行,还能随时换猎头,特别灵活,这对应到代码里,就是调用方和具体执行方法的解耦,提高代码的灵活性和可维护性。"。
委托解决的核心技术问题(面试结合例子答,更加分)
结合生活例子,委托在编程中解决的核心问题是消除方法之间的硬耦合,实现程序的灵活扩展和动态执行,具体体现在:
- 将"做什么"和"谁来做、怎么做"分离:定义委托(定规则/做什么),后续可灵活绑定不同的方法(找不同的执行者/谁来做),无需修改调用方的代码;
- 支持动态替换/扩展执行者 :通过
+=/-=随时添加/移除方法,程序运行时可动态调整执行逻辑,符合面向对象的"开闭原则"; - 实现方法的抽象和复用:将方法作为参数传递,让通用逻辑(如集合遍历、异步执行)可以复用,无需为每个具体方法写重复的调用代码。
9. 委托的概念 对应 java 里或者java 安卓开发里 是什么东西,有类似的东西吗
面试口语化回答(直接用)
C#中的委托在Java/Java安卓开发中没有完全一致的语法级特性,但有功能上高度等价的实现:
- Java基础层面,函数式接口(SAM接口) 是最贴合的核心设计,结合Lambda表达式、方法引用,能实现委托的所有核心功能------方法作为参数传递、回调、解耦,Java还内置了
Consumer、Function等通用接口,对应C#的Action、Func预定义委托; - 而Java安卓开发中,日常使用的各种OnXxxListener(如OnClickListener)、自定义Callback回调接口,本质都是函数式接口的具体应用,是委托在安卓业务中的落地形式,比如按钮点击、网络请求回调,都是用这种方式实现解耦的;
- 另外,C#委托天生支持多播,Java没有原生支持,但可以通过维护函数式接口的集合,手动实现多播的效果。
简单说,C#委托 ≈ Java函数式接口 + Lambda/匿名内部类 ≈ 安卓的各种Listener/Callback接口。
一、Java 基础中:函数式接口(核心等价体)
Java 8 引入函数式接口(也叫 SAM 接口:Single Abstract Method),是专门为 "将方法作为参数传递、实现回调" 设计的接口,也是对 C# 委托最直接的功能替代。
可加@FunctionalInterface注解显式声明,编译器会校验规则。
java
// Java 函数式接口实现
// 1. 声明函数式接口(定签名,对应C#委托类型)
@FunctionalInterface
interface HelloFunctionalInterface {
void say(); // 唯一抽象方法,对应委托的签名
}
// 2. 定义匹配签名的方法
public static void sayHello() {
System.out.println("Java 函数式接口执行");
}
// 3. 接口作为参数传递(核心功能,对应C#委托传参)
public static void execute(HelloFunctionalInterface fi) {
fi.say(); // 调用接口方法,对应C#的Invoke()
}
// 4. 调用:3种赋值方式(对应C#的委托赋值)
public static void main(String[] args) {
// 方式1:匿名内部类(Java8前的主流写法,对应C#早期的匿名方法)
execute(new HelloFunctionalInterface() {
@Override
public void say() {
sayHello();
}
});
// 方式2:Lambda表达式(Java8+主流,对应C#的Lambda委托,最简洁)
execute(() -> System.out.println("Java Lambda执行"));
// 方式3:方法引用(对应C#的方法组隐式转换,更简洁)
execute(JavaDelegate::sayHello);
}
简单说:C#委托 ≈ Java函数式接口 + 匿名内部类/Lambda/方法引用 ,安卓里的OnClickListener、OnSuccessListener等,就是委托在安卓开发中的"具体应用版"。
场景2:有参有返回的业务逻辑(如计算)
csharp
// C# 委托
delegate int CalculateDelegate(int a, int b);
static int Add(int a, int b) => a + b;
static void Cal(CalculateDelegate del, int a, int b) => Console.WriteLine(del(a,b));
// 调用
Cal(Add, 2,3); // 输出5
Cal((a,b)=>a*b,2,3); // 输出6
java
// Java 函数式接口
@FunctionalInterface
interface CalculateFunctionalInterface {
int cal(int a, int b);
}
public static int add(int a, int b) { return a + b; }
public static void cal(CalculateFunctionalInterface cfi, int a, int b) {
System.out.println(cfi.cal(a, b));
}
// 调用
public static void main(String[] args) {
cal(JavaDelegate::add, 2,3); // 方法引用,输出5
cal((a,b)->a*b,2,3); // Lambda,输出6
}
3. Java内置的函数式接口(无需自定义,对应C#预定义委托)
C#有预定义委托(Action无返回、Func有返回),Java也内置了大量通用函数式接口,放在java.util.function包下,开发中无需自定义,直接使用,核心的两个和C#完全对应:
| C# 预定义委托 | Java 内置函数式接口 | 核心特点 |
|---|---|---|
Action<T> |
Consumer<T> |
有参、无返回值(消费参数) |
Func<T,R> |
Function<T,R> |
有参、有返回值(参数映射为返回值) |
Action |
Runnable/Supplier<Void> |
无参、无返回值 |
二、Java安卓开发中:各种Listener/Callback(委托的实际应用版)
安卓开发中几乎不会直接说"函数式接口",但日常写的所有回调接口(OnXxxListener、XxxCallback) ,本质都是自定义的函数式接口,是C#委托在安卓开发中最常见、最贴近业务的落地形式,也是安卓解耦的核心手段(和C#委托的解耦目的一致)。
安卓中这些接口的使用场景,完全对应C#委托的使用场景,比如UI点击、网络请求回调、异步任务回调,举两个安卓开发中最经典的例子:
示例1:安卓UI点击事件(OnClickListener)
安卓按钮点击的OnClickListener是最典型的函数式接口,对应C#中"给按钮绑定点击委托方法",实现UI触发→业务方法执行的解耦:
java
// 安卓代码:Button点击事件(OnClickListener是函数式接口,仅一个onClick方法)
Button btn = findViewById(R.id.btn_click);
// Lambda赋值(Java8+安卓支持,对应C#的委托Lambda赋值)
btn.setOnClickListener(v -> {
Toast.makeText(this, "按钮被点击", Toast.LENGTH_SHORT).show();
});
// 等价于Java8前的匿名内部类写法(安卓早期主流)
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "按钮被点击", Toast.LENGTH_SHORT).show();
}
});
对应C# WinForm/WPF的按钮点击委托:
csharp
Button btn = new Button();
// C# 委托绑定点击方法
btn.Click += (s, e) => MessageBox.Show("按钮被点击");
示例2:安卓网络请求回调(自定义Callback)
安卓中网络请求(如Retrofit、OkHttp)的回调接口,是函数式接口的典型应用,对应C#中"异步请求的委托回调",实现网络框架和业务逻辑的解耦:
java
// 1. 自定义回调接口(函数式接口,对应C#委托类型)
public interface OnRequestCallback {
void onSuccess(String result); // 成功回调
void onError(String errorMsg); // 失败回调(多方法可拆分为多个单方法接口)
}
// 2. 网络请求方法:将回调接口作为参数(对应C#委托传参)
public void requestData(String url, OnRequestCallback callback) {
// 模拟网络请求
new Thread(() -> {
try {
// 模拟请求成功
String result = "请求到的数据";
runOnUiThread(() -> callback.onSuccess(result));
} catch (Exception e) {
runOnUiThread(() -> callback.onError(e.getMessage()));
}
}).start();
}
// 3. 调用:绑定回调逻辑(对应C#委托赋值)
requestData("https://xxx.com/api", new OnRequestCallback() {
@Override
public void onSuccess(String result) {
// 业务层处理成功数据,和网络层完全解耦
tvResult.setText(result);
}
@Override
public void onError(String errorMsg) {
Toast.makeText(MainActivity.this, "请求失败:" + errorMsg, Toast.LENGTH_SHORT).show();
}
});
这个场景中,OnRequestCallback就是安卓版的"委托类型",网络请求方法只负责请求,不关心成功/失败后业务层做什么,完全由调用方通过回调接口定义,和C#委托的解耦核心思想完全一致。
三、关于「多播委托」的Java实现
C#委托天生支持多播 (一个委托变量绑定多个方法,按序执行),Java没有原生支持这个特性,但可以基于函数式接口手动实现多播效果(面试中可补充这个点,体现深度)。
核心思路:自定义一个"多播包装类",内部维护一个函数式接口的集合 ,提供add(对应C# +=)、remove(对应C# -=)方法,调用时遍历集合执行所有接口方法。
示例:Java手动实现多播委托(对应C#的多播功能)
java
// 1. 基础函数式接口(定签名)
@FunctionalInterface
interface HelloFunctionalInterface {
void say();
}
// 2. 多播包装类(实现+=、-=、遍历执行)
class MulticastDelegate {
// 维护函数式接口集合
private final List<HelloFunctionalInterface> delegateList = new ArrayList<>();
// 添加方法(对应C# +=)
public void add(HelloFunctionalInterface fi) {
delegateList.add(fi);
}
// 移除方法(对应C# -=)
public void remove(HelloFunctionalInterface fi) {
delegateList.remove(fi);
}
// 执行所有方法(对应C# Invoke(),按添加顺序执行)
public void invoke() {
for (HelloFunctionalInterface fi : delegateList) {
fi.say();
}
}
}
// 3. 使用测试
public class JavaMulticast {
public static void sayHello1() { System.out.println("执行方法1"); }
public static void sayHello2() { System.out.println("执行方法2"); }
public static void main(String[] args) {
MulticastDelegate multicast = new MulticastDelegate();
// 添加方法(多播)
multicast.add(JavaMulticast::sayHello1);
multicast.add(JavaMulticast::sayHello2);
multicast.add(() -> System.out.println("执行Lambda方法3"));
// 一次调用,按序执行所有方法(多播核心)
multicast.invoke();
// 移除方法
multicast.remove(JavaMulticast::sayHello1);
System.out.println("---移除方法1后---");
multicast.invoke();
}
}
输出结果:
执行方法1
执行方法2
执行Lambda方法3
---移除方法1后---
执行方法2
执行Lambda方法3
这个实现完全复刻了C#多播委托的核心功能,安卓开发中如果需要多播效果,也可以用这个思路实现。
四、核心对比总结(面试速记)
| 特性 | C# 委托 | Java/安卓 |
|---|---|---|
| 核心定义方式 | delegate关键字声明委托类型 |
函数式接口(SAM)声明签名 |
| 赋值方式 | 方法组、Lambda、new委托实例 | 方法引用、Lambda、匿名内部类 |
| 预定义通用类型 | Action(无返回)、Func(有返回) |
Consumer、Function(java.util.function) |
| 安卓实际应用 | 委托绑定事件/回调 | OnXxxListener、XxxCallback接口 |
| 多播支持 | 天生支持(+=/-=) |
无原生支持,可基于集合手动实现 |
| 核心功能 | 方法传参、回调、解耦、多播 | 方法传参、回调、解耦(多播手动实现) |
lambda 表达式, 怎么看有无参数,有无返回值?
基本语法
1 参数列表 => 表达式
例如:
n => n * n // 有一个参数 n,返回 (n * n) 的值
() => 42 // 无参数,返回 42
() => Console.WriteLine("hi") // 无参数,无返回值(因表达式为 void 方法调用),可赋给(Action)
2. lambda (多语句)
参数列表 => { 语句1; 语句2; ... }
## 预定义的委托 有哪些?
Action ,泛型 Action :无返回值,可以有参数
泛型 Func 特点 肯定是有返回值,可以没有参数
泛型Predicate 特点 返回值是 布尔值,
## Action ,Func 有啥考点 todo
一、Lambda 表达式:快速判断「有无参数、有无返回值」
核心判断逻辑看语法结构即可,无需死记,结合「参数列表格式」和「右侧体(表达式/语句块)的执行结果」
步骤1:判断「有无参数」→ 看左侧参数列表
- 无参数 :左侧必须显式写空括号
(),是唯一判定标志; - 有参数 :单参数可省括号(
n => ...),多参数必须带括号且用逗号分隔((a,b) => ...); - 特殊:参数类型可省(编译器自动推断),写了也不影响判断(
(int a) => ...仍为单参数)。
步骤2:判断「有无返回值」→ 看右侧体(表达式/语句块)
Lambda 的返回值由右侧执行结果决定,编译器会自动匹配委托的返回值签名,分两种情况:
- 右侧是「单个表达式」 :无需写
return,表达式的结果就是返回值(无结果则为无返回); - 右侧是「语句块」(带
{}) :有返回值需显式写return,无return且无执行结果则为无返回。
() => Console.WriteLine("hi") // 无参数,无返回值(因表达式为 void 方法调用),可赋给
快速判断示例(面试常考场景,覆盖所有情况)
| Lambda 表达式 | 参数判断 | 返回值判断 | 匹配的预定义委托 |
|---|---|---|---|
() => 42 |
无参数 | 有返回(int) | Func<int> |
() => Console.WriteLine("hi") |
无参数 | 无返回(void) | Action |
n => n * n |
单参数 | 有返回(同n类型) | Func<T,T>(如Func<int,int>) |
(a,b) => a + b |
多参数 | 有返回(同a/b类型) | Func<T1,T2,TResult>(如Func<int,int,int>) |
s => { Console.WriteLine(s); } |
单参数 | 无返回 | Action<string> |
(x,y) => { return x > y; } |
多参数 | 有返回(bool) | Func<int,int,bool>/Predicate |
二、Action/Func 核心总结(用法+注意事项)
Action 和 Func 是C#最常用的预定义委托 ,目的是替代自定义委托 ,开发中90%的委托场景都能直接用,核心特点用一句话记:Action无返回,Func必有返回。
Action 系列:无返回值,支持0~16个参数
- 非泛型:
Action→ 无参数、无返回,对应delegate void 委托名();; - 泛型:
Action<T1,T2...>→ 泛型参数为参数类型 ,无返回,支持0~16个参数,对应delegate void 委托名(T1 p1,T2 p2...);; - 核心用法:多播委托、事件绑定、无返回的回调(如打印、日志、UI更新)。
Func 系列:必有返回值,支持0~16个参数
- 泛型参数规则:最后一个泛型参数是返回值类型,前面的都是参数类型(无参数则只有返回值泛型);
- 核心形式:
Func<TResult>(无参有返回)、Func<T1,T2,TResult>(多参有返回); - 核心用法:有返回的业务逻辑回调(如计算、数据查询、值转换)。
补充:Predicate
- 特殊的Func:等价于
Func<T,bool>,返回值固定为bool; - 专属场景:集合查找(如
List.Find(Predicate<T>)),语义更清晰。
2. 核心注意事项(面试必答,简洁记点)
- 签名匹配硬性要求 :Lambda/绑定的方法,参数类型/个数、返回值类型必须和Action/Func泛型参数完全一致;
- 调用前必须判空 :Action/Func都是引用类型,默认
null,推荐用?.Invoke()避免空引用异常;
1. Action 和 Func 最核心的区别是什么?
参考答案 :核心区别在返回值------Action系列无返回值(void) ,泛型参数仅代表参数类型;Func系列必有返回值,泛型参数的最后一个固定为返回值类型,前面的是参数类型。此外,Action更适合无返回的回调/多播,Func适合有返回的业务逻辑回调。
4. 为什么多播委托通常使用Action,而不是Func?
参考答案 :因为C#多播委托调用时,只会返回最后一个绑定方法的返回值,前面所有方法的返回值都会被丢弃;Action无返回值,不会有返回值丢失的问题,而Func有返回值,丢失后无实际意义,所以多播委托优先用Action。
2. Func 的泛型参数有什么特殊规则?请举例说明。
参考答案 :Func的泛型参数最后一个是返回值类型,前面的所有参数都是委托的入参类型;如果无入参,就只有一个表示返回值的泛型参数。
3. 请用Func实现两数相乘的逻辑,用Action实现打印该乘积的逻辑,要求结合Lambda。
参考答案:
csharp
// Func实现相乘,两int入参,返回int
Func<int, int, int> multiply = (a, b) => a * b;
// Action实现打印,int入参,无返回
Action<int> printResult = res => Console.WriteLine($"乘积:{res}");
// 调用
int res = multiply(3,4);
printResult(res); // 输出:乘积:12
5. Action/Func 和自定义委托有什么区别?开发中什么时候用自定义委托,什么时候用Action/Func?
参考答案:
- 本质区别:Action/Func是C#内置的通用预定义委托,无需手动声明;自定义委托是通过
delegate关键字手动声明的专属委托,可自定义名称和语义; - 使用场景:
- 用Action/Func:简单回调、临时逻辑(如直接绑定Lambda)、通用场景(如打印、计算),减少重复代码;
- 用自定义委托:复杂业务场景(如订单回调、支付回调),需要语义化命名 (如
OrderCallback比Action<Order>更易读)、需要给委托添加注释说明、团队协作需要明确委托的业务含义时。
6. 能否将一个有返回值的方法绑定到Action?为什么?
参考答案 :不能。因为Action的核心特性是无返回值(void),委托的签名要求方法的参数类型/个数、返回值类型必须完全匹配;有返回值的方法和Action的返回值签名不匹配,编译器会直接报错。
四、核心速记(面试前快速过)
- Lambda判断:看()知参数,看右侧体/return知返回值,语句块有返回必须写return;
- Action:无返回,泛型参数=入参类型,多播/无返回回调首选;
- Func:必有返回,泛型参数「前入参,后返回」,有返回业务逻辑首选;
- 核心原则:签名必须完全匹配 ,调用前必判空(
?.Invoke()); - 替代规则:简单通用场景用Action/Func,复杂语义化场景用自定义委托。