委托和事件笔记

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#内置的intstring,以及自定义类PersonStudent的类型特性完全一致;

声明委托变量的语法和声明普通类型变量完全相同,委托类型 + 变量名 即可,委托变量的默认值为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属性会指向该类的实例对象 (面试考点);若绑定静态方法,Targetnull

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类的委托链实现。

多播委托的使用

  1. +=向委托链添加方法 ,用-=从委托链移除方法
  2. 调用时(Invoke()/直接变量名),会按添加顺序依次执行委托链中的所有方法;
  3. 移除方法时,若方法不在委托链中,不会抛出异常;
  4. 调用前必须判空(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点:

  1. 支持多播执行:一个委托调用可执行多个方法,无需手动逐个调用,简化批量方法执行逻辑;
  2. 实现方法解耦:调用方无需知道具体要执行哪个/哪些方法,只需和委托交互,符合"开闭原则";
  3. 方法作为参数传递 :可将方法当作参数传入其他方法,实现回调逻辑(如异步回调、集合遍历回调);
  4. 动态管理方法 :通过+=/-=运行时动态添加/移除执行方法,无需修改代码逻辑;
  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. 多播与异常? 注意事项?

  1. 移除方法时,若方法不在委托链中,不会抛出异常;
  2. 调用前必须判空(obj?.Invoke()),避免委托链为null时的空引用异常。
  3. 多播委托中,若某个方法抛出异常,委托链中后续方法会停止执行

8. 举几个生活中的例子说明委托?委托解决的核心技术问题是什么?

生活中的委托例子(贴合委托核心逻辑,面试口语化回答即可)

委托的生活逻辑:自己不亲自做某件事,委托给符合"规则"的其他人/机构做,可委托一个,也可委托多个,还能随时更换被委托人,核心是「定规则、找执行者、解耦自己和执行者」,3个易懂例子:

  1. 公司招聘:比如公司招 Java 开发,先定好岗位要求这个统一规则,这就是委托的签名;我们公司和另一家公司都按这个规则找同一个猎头,就是不同的委托变量绑定同一个方法;如果我们公司同时找两个猎头,就是多播委托,一次委托两个猎头都帮我们找人。而委托解决的核心问题,就是把公司和候选人解耦了,我们不用自己找候选人,只对接猎头就行,还能随时换猎头,特别灵活,这对应到代码里,就是调用方和具体执行方法的解耦,提高代码的灵活性和可维护性。"。

委托解决的核心技术问题(面试结合例子答,更加分)

结合生活例子,委托在编程中解决的核心问题是消除方法之间的硬耦合,实现程序的灵活扩展和动态执行,具体体现在:

  1. 将"做什么"和"谁来做、怎么做"分离:定义委托(定规则/做什么),后续可灵活绑定不同的方法(找不同的执行者/谁来做),无需修改调用方的代码;
  2. 支持动态替换/扩展执行者 :通过+=/-=随时添加/移除方法,程序运行时可动态调整执行逻辑,符合面向对象的"开闭原则";
  3. 实现方法的抽象和复用:将方法作为参数传递,让通用逻辑(如集合遍历、异步执行)可以复用,无需为每个具体方法写重复的调用代码。

9. 委托的概念 对应 java 里或者java 安卓开发里 是什么东西,有类似的东西吗

面试口语化回答(直接用)

C#中的委托在Java/Java安卓开发中没有完全一致的语法级特性,但有功能上高度等价的实现:

  1. Java基础层面,函数式接口(SAM接口) 是最贴合的核心设计,结合Lambda表达式、方法引用,能实现委托的所有核心功能------方法作为参数传递、回调、解耦,Java还内置了ConsumerFunction等通用接口,对应C#的ActionFunc预定义委托;
  2. 而Java安卓开发中,日常使用的各种OnXxxListener(如OnClickListener)、自定义Callback回调接口,本质都是函数式接口的具体应用,是委托在安卓业务中的落地形式,比如按钮点击、网络请求回调,都是用这种方式实现解耦的;
  3. 另外,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/方法引用 ,安卓里的OnClickListenerOnSuccessListener等,就是委托在安卓开发中的"具体应用版"。

场景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(有返回) ConsumerFunctionjava.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 的返回值由右侧执行结果决定,编译器会自动匹配委托的返回值签名,分两种情况:

  1. 右侧是「单个表达式」 :无需写 return,表达式的结果就是返回值(无结果则为无返回);
  2. 右侧是「语句块」(带 {} :有返回值需显式写 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 核心总结(用法+注意事项)

ActionFunc 是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. 核心注意事项(面试必答,简洁记点)

  1. 签名匹配硬性要求 :Lambda/绑定的方法,参数类型/个数、返回值类型必须和Action/Func泛型参数完全一致
  2. 调用前必须判空 :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关键字手动声明的专属委托,可自定义名称和语义;
  • 使用场景:
    1. 用Action/Func:简单回调、临时逻辑(如直接绑定Lambda)、通用场景(如打印、计算),减少重复代码;
    2. 用自定义委托:复杂业务场景(如订单回调、支付回调),需要语义化命名 (如OrderCallbackAction<Order>更易读)、需要给委托添加注释说明、团队协作需要明确委托的业务含义时。

6. 能否将一个有返回值的方法绑定到Action?为什么?

参考答案 :不能。因为Action的核心特性是无返回值(void),委托的签名要求方法的参数类型/个数、返回值类型必须完全匹配;有返回值的方法和Action的返回值签名不匹配,编译器会直接报错。


四、核心速记(面试前快速过)

  1. Lambda判断:看()知参数,看右侧体/return知返回值,语句块有返回必须写return;
  2. Action:无返回,泛型参数=入参类型,多播/无返回回调首选;
  3. Func:必有返回,泛型参数「前入参,后返回」,有返回业务逻辑首选;
  4. 核心原则:签名必须完全匹配 ,调用前必判空(?.Invoke());
  5. 替代规则:简单通用场景用Action/Func,复杂语义化场景用自定义委托。
相关推荐
奥特曼_ it4 小时前
【数据分析+机器学习】基于机器学习的招聘数据分析可视化预测推荐系统(完整系统源码+数据库+开发笔记+详细部署教程)✅
笔记·数据挖掘·数据分析
雨季6664 小时前
构建 OpenHarmony 简易文字行数统计器:用字符串分割实现纯文本结构感知
开发语言·前端·javascript·flutter·ui·dart
雨季6664 小时前
Flutter 三端应用实战:OpenHarmony 简易倒序文本查看器开发指南
开发语言·javascript·flutter·ui
小北方城市网4 小时前
Redis 分布式锁高可用实现:从原理到生产级落地
java·前端·javascript·spring boot·redis·分布式·wpf
进击的小头4 小时前
行为型模式:策略模式的C语言实战指南
c语言·开发语言·策略模式
天马37985 小时前
Canvas 倾斜矩形绘制波浪效果
开发语言·前端·javascript
六义义5 小时前
java基础十二
java·数据结构·算法
四维碎片5 小时前
QSettings + INI 笔记
笔记·qt·算法
Tansmjs5 小时前
C++与GPU计算(CUDA)
开发语言·c++·算法
qx095 小时前
esm模块与commonjs模块相互调用的方法
开发语言·前端·javascript