返回题库高级 .NET 刷题C# 选择题 · 第 17 / 50 篇

C# 试题 17:委托与 Lambda

0321 以下代码输出什么?

难度: 实战

Action a = () => Console.Write("x");
Action b = () => Console.Write("x");
Console.WriteLine(a == b);
  • A. False,两个 lambda 表达式生成不同的委托实例
  • B. True,方法体相同所以相等
  • C. 编译失败
  • D. 运行抛异常
查看答案与解析

正确答案A

解析: 每个 lambda 表达式都会编译成独立的(编译器生成的)方法与独立委托实例;委托相等比较的是目标方法加目标对象,两个不同方法自然不相等。即使方法体完全一样,a == b 也是 False

关键点: 相同方法体的不同 lambda 表达式互不相等。

0322 以下代码输出什么?

难度: 进阶

Action a = M;
Action b = M;
Console.WriteLine(a == b);
static void M() { }
  • A. False
  • B. True,同一方法组、同一目标的方法产生相等委托
  • C. 编译失败
  • D. 运行抛异常
查看答案与解析

正确答案B

解析: 方法组转换的委托相等性取决于目标方法与目标对象:两个转换都指向静态方法 M(目标为 null),语义上相等,输出 True。这与上题”相同方法体的 lambda 不相等”形成对比——lambda 每次生成新方法,方法组指向同一方法。

关键点: 方法组转换产生指向同一方法的委托,比较相等。

0323 以下代码输出什么?

难度: 实战

Action a = () => Console.Write("1");
Action b = a;
a += () => Console.Write("2");
a();
b();
  • A. 1212
  • B. 11
  • C. 121+= 生成新的多播委托,b 仍指向原委托
  • D. 编译失败
查看答案与解析

正确答案B

解析: 委托是不可变的:a += ... 创建新的多播委托并重新赋值给 ab 仍持有旧的单播委托。因此 a() 输出 12b() 输出 1。把委托当可变列表改是常见误解。

关键点: 委托不可变,+=/-= 返回新实例,旧引用不受影响。

0324 以下代码能否编译?

难度: 进阶

int x = 42;
Func<int> f = static () => x;
  • A. 编译通过,调用输出 42
  • B. 编译通过,但调用时抛异常
  • C. 编译通过,static 修饰被忽略
  • D. 编译失败,静态 lambda 不能捕获局部变量
查看答案与解析

正确答案D

解析: C# 9 的静态 lambda(static () => ...)禁止捕获实例状态与局部变量,引用 x 直接编译失败。它的价值是消除闭包对象的分配,但代价是只能用参数和静态成员。

关键点: 静态 lambda 不能捕获,捕获就编译失败。

0325 关于静态 lambda(static () => ...),哪项正确?

难度: 进阶

  • A. 不能捕获实例状态与局部变量,但可以使用参数和静态成员
  • B. 与普通 lambda 完全相同,static 只是装饰
  • C. 只能用于无参 lambda
  • D. 静态 lambda 不能访问静态字段
查看答案与解析

正确答案A

解析: static 修饰只限制捕获:实例成员、局部变量、this 都不能引用;参数与静态成员不受限。例如 static (int y) => y + StaticField 合法。它主要用于避免闭包分配,适合高频调用路径。

关键点: 静态 lambda = 无捕获的 lambda,可用参数与静态成员。

0326 以下代码输出什么?

难度: 实战

var actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
    actions.Add(() => Console.Write(i));
}
foreach (var a in actions)
{
    a();
}
  • A. 333
  • B. 012,C# 5 起 for 循环变量每次迭代都是新变量
  • C. 222
  • D. 编译失败
查看答案与解析

正确答案B

解析: C# 5 起 for 循环的迭代变量在每次迭代都生成新变量,每个闭包捕获各自的一份,输出 012。若手写 while 共享同一个变量,仍会得到 333——问题在变量是否每轮新建,而非”循环自动安全”。

关键点: for/foreach 的迭代变量按迭代隔离,闭包捕获的是各自副本。

0327 以下代码输出什么?

难度: 实战

int x = 1;
Func<int> f = () => x;
x = 2;
Console.WriteLine(f());
  • A. 1
  • B. 0
  • C. 2,闭包按引用捕获变量而非值快照
  • D. 编译失败
查看答案与解析

正确答案C

解析: 闭包捕获的是变量本身(编译器提升为闭包对象的字段),调用时读取的是当前值。捕获发生在创建时,但读值发生在调用时,所以先改 x 再调用得到 2。“捕获即快照”是常见误判。

关键点: 闭包引用变量,不复制值;值在调用时读取。

0328 以下代码输出什么?

难度: 进阶

int x = 0;
Func<int> a = () => ++x;
Func<int> b = () => ++x;
a();
b();
Console.WriteLine(x);
  • A. 0
  • B. 1
  • C. 编译失败
  • D. 2,两个闭包共享同一个被捕获变量
查看答案与解析

正确答案D

解析: 两个 lambda 捕获同一个 x,编译器只创建一个闭包对象,x 是其共享字段;a()b() 各自自增一次,最终 x == 2。多个闭包共享捕获变量时,它们的读写互相可见。

关键点: 捕获同一变量的多个闭包共享该变量,不是各自副本。

0329 以下代码输出什么?

难度: 实战

var f = (int x) => x * 2;
Console.WriteLine(f(21));
  • A. 42,自然类型推断为 Func<int, int>
  • B. 21
  • C. 编译失败,var 不能用于 lambda
  • D. 运行抛异常
查看答案与解析

正确答案A

解析: C# 10 起 lambda 有自然类型:参数全部显式声明类型时,编译器能推断出 Func<int, int>var f = (int x) => x * 2; 合法并输出 42。C# 10 之前 var 声明 lambda 无法编译。

关键点: 显式参数类型的 lambda 可用 var 获得自然类型。

0330 以下代码能否编译?

难度: 进阶

var f = x => x * 2;
  • A. 编译通过,x 推断为 int
  • B. 编译失败,参数无类型时无法推断自然委托类型
  • C. 编译通过,x 推断为 object
  • D. 编译通过,但运行时抛异常
查看答案与解析

正确答案B

解析: 自然类型要求参数类型可知:x => ...x 没有显式类型,也没有可套用的目标委托类型(var 不提供目标类型),编译器无法推断,报错。这与 Func<int,int> f = x => x * 2;(有目标类型,合法)不同。

关键点: 隐式参数类型需要目标类型,var 推断自然类型时参数必须显式声明。

0331 以下代码输出什么?

难度: 进阶

Expression<Func<int, int>> e = x => x + 1;
var f = e.Compile();
Console.WriteLine(f(1));
  • A. 编译失败
  • B. 1
  • C. 2,表达式树可编译为可调用委托
  • D. 运行抛异常
查看答案与解析

正确答案C

解析: 赋给 Expression<Func<int,int>> 时 lambda 被表示为表达式树,Compile() 把树编译为委托,f(1) = 1 + 1 = 2。表达式树不是”存了 lambda”,而是把逻辑拆成可检查的树节点。

关键点: Expression<...> 存的是树,Compile() 之后才可调用。

0332 以下代码能否编译?

难度: 实战

Expression<Func<int, int>> e = x => { return x + 1; };
  • A. 编译通过
  • B. 编译通过,运行抛异常
  • C. 编译通过,但编译后返回 null
  • D. 编译失败,语句体 lambda 不能转换为表达式树
查看答案与解析

正确答案D

解析: 表达式树只能表示表达式,不能表示语句块;{ return x + 1; } 是语句体 lambda,赋给 Expression<...> 报 CS0834。这是 ORM 场景”为什么必须写表达式写法”的原因之一。

关键点: 表达式树要求表达式体 lambda,语句体一律编译失败。

0333 以下代码能否编译?

难度: 进阶

int y = 0;
Expression<Func<int>> e = () => y++;
  • A. 编译失败,表达式树不能包含赋值/自增操作
  • B. 编译通过,调用后 y == 1
  • C. 编译通过,调用后 y == 0
  • D. 编译通过,运行抛异常
查看答案与解析

正确答案A

解析: 表达式树不允许赋值类操作(=++-- 等),报 CS0832。注意表达式树允许引用被捕获的变量(会编译成字段访问),所以”表达式树不能捕获变量”也是误区——不能的是赋值,不是读取

关键点: 表达式树可读捕获变量,但不可写。

0334 以下代码输出什么?

难度: 实战

Action a = () => Console.Write("1");
a += () => throw new InvalidOperationException();
a += () => Console.Write("3");
try
{
    a();
}
catch (InvalidOperationException)
{
}
  • A. 输出 13
  • B. 输出 1 后抛异常,“3”不会执行
  • C. 输出 123
  • D. 编译失败
查看答案与解析

正确答案B

解析: 多播委托按顺序调用订阅者,一旦某个订阅者抛异常,调用立即中止,后续订阅者(“3”)不会执行,异常沿调用方传播。没有”事件框架帮你跳过异常订阅者”这回事。

关键点: 多播委托遇到异常即中止,后续订阅者不执行。

0335 以下代码输出什么?

难度: 进阶

Action a = null;
a += () => Console.Write("1");
a -= () => Console.Write("1");
a?.Invoke();
  • A. 什么都不输出
  • B. 运行抛异常
  • C. 输出 1-= 中的新 lambda 是不同委托实例,无法匹配移除
  • D. 编译失败
查看答案与解析

正确答案C

解析: -= 右边的 lambda 是全新表达式,编译成不同方法、不同委托实例,与已订阅的委托不相等,Delegate.Remove 找不到匹配项,原多播委托原样保留,输出 1。匿名 lambda 无法退订,必须保存委托引用或使用方法组。

关键点: 用新 lambda 退订等于没退订。

0336 关于委托实例的相等性,哪项正确?

难度: 进阶

  • A. 只要方法体相同,委托就相等
  • B. 委托相等性没有定义
  • C. 委托总是按引用比较,内容无关
  • D. 委托相等取决于委托类型、目标方法与目标对象;同一实例的同一方法才相等
查看答案与解析

正确答案D

解析: Delegate.Equals 比较类型、目标方法(Method)与目标对象(Target):obj.Mobj.M 相等,obj1.Mobj2.M 不相等(目标不同)。lambda 每次生成新方法,所以相同方法体的两个 lambda 委托不相等。多播委托还要比较整个调用列表。

关键点: 委托相等 = 类型 + 方法 + 目标对象,不是方法体文本。

0337 以下代码输出什么?

难度: 进阶

static Func<int> Make()
{
    int x = 0;
    return () => x;
}
var f1 = Make();
var f2 = Make();
Console.WriteLine(f1 == f2);
  • A. False,每次调用 Make 创建新的闭包对象
  • B. True,编译器缓存了闭包
  • C. 编译失败
  • D. 运行抛异常
查看答案与解析

正确答案A

解析: 捕获变量的 lambda 需要闭包对象承载变量;Make 每次调用都 new 一个闭包实例,委托目标(Target)不同,因此不相等。只有不捕获的 lambda 才会被缓存为单一静态委托实例。

关键点: 捕获型闭包每次调用新建,无捕获 lambda 才可能缓存复用。

0338 以下代码能否编译?

难度: 进阶

object o = (int x) => x;
  • A. 编译通过,o 得到 Func<int, int>
  • B. 编译失败,lambda 不能直接转换为 object
  • C. 编译通过,但 o 为 null
  • D. 编译通过,运行抛异常
查看答案与解析

正确答案B

解析: lambda 只能转换为委托类型或表达式树类型,即使它有自然类型,也不能隐式转换为 object(CS1660)。必须先赋给委托再装箱,如 object o = (Func<int,int>)(x => x);

关键点: 有自然类型 ≠ 能转 object,lambda 的目标类型必须是委托/表达式树。

0339 以下代码输出什么?

难度: 实战

Action a = null;
a += M1;
a += M2;
a += M1;
a -= M1;
a();
static void M1() => Console.Write("1");
static void M2() => Console.Write("2");
  • A. 112
  • B. 1
  • C. 12-= 移除的是最后一个匹配项
  • D. 21
查看答案与解析

正确答案C

解析: 调用列表是 [M1, M2, M1]-=Delegate.Remove 移除最靠后的匹配项,得到 [M1, M2],调用输出 12。多播列表允许重复项,“移除一个”不等于”清空所有匹配项”。

关键点: -= 移除最后一个匹配项,重复订阅需要逐个移除。

0340 关于 Lambda 与委托,哪项正确?

难度: 进阶

  • A. Lambda 表达式是值类型
  • B. 委托实例总是互不相等
  • C. 不同 lambda 表达式生成不同方法/委托;方法组转换的同一方法与同一目标则产生相等委托
  • D. 委托只能在栈上使用
查看答案与解析

正确答案C

解析: lambda 是语法糖,每个表达式编译成独立方法(捕获时外加闭包类型),其委托自然互不相等;方法组转换直接指向既有方法,同一方法同一目标时相等。委托是引用类型、分配在堆上,不是值类型。

关键点: lambda 生成新方法 ⇒ 委托不相等;方法组指向既有方法 ⇒ 可相等。

官方资料

当前分类

C# 选择题

查看全部分类 →
  1. 15C# 试题 15:泛型基础20 题
  2. 16C# 试题 16:泛型约束与型变20 题
  3. 17C# 试题 17:委托与 Lambda20 题
  4. 18C# 试题 18:事件20 题
  5. 19C# 试题 19:异常处理20 题
ESC

输入关键词开始搜索