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

C# 试题 40:dynamic 与表达式树

0781 以下代码输出什么?

难度: 进阶

dynamic d = null;

try
{
    d.ToString();
}
catch (Exception ex)
{
    Console.WriteLine(ex.GetType().Name);
}
  • A. 抛 NullReferenceException
  • B. 编译失败,不能对 null 调用成员
  • C. 无异常,输出空行
  • D. 抛 RuntimeBinderException,dynamic 在 null 引用上无法完成绑定
查看答案与解析

正确答案D

解析: dynamic 的成员访问在运行时绑定,目标为 null 时绑定无法完成,抛 RuntimeBinderException(消息为 “Cannot perform runtime binding on a null reference”)。只有静态类型为 null 的引用调用实例成员才抛 NullReferenceException

关键点: dynamic 对 null 调用成员抛 RuntimeBinderException,不是 NullReferenceException

0782 以下代码输出什么?

难度: 进阶

dynamic d = 42;
var v = d;
v += 1;

Console.WriteLine(v);
  • A. 输出 43var 推断出的类型是 dynamic,v += 1 走运行时绑定
  • B. 编译失败,var 无法从 dynamic 推断类型
  • C. 输出 421
  • D. 运行抛 RuntimeBinderException
查看答案与解析

正确答案A

解析: var 推断的是初始化表达式的静态类型,d 是 dynamic,所以 v 也是 dynamic;v += 1 在运行时按真实类型 int 绑定加法,输出 43。若先 object o = d; var v = o;,则 v 是 object,加法将无法编译。

关键点: dynamic 会“传染”给 varvar 不会把 dynamic 拆成 object。

0783 以下代码能否编译?若能,输出什么?

难度: 实战

object o = 10;
dynamic d = 10;

Console.WriteLine(o + 1);
Console.WriteLine(d + 1);
  • A. 两行都编译失败
  • B. 两行都输出 11
  • C. 第一行编译失败,第二行输出 11
  • D. 第一行输出 10,第二行输出 101
查看答案与解析

正确答案C

解析: o 的静态类型是 object,+ 运算符对 object 未定义,o + 1 在编译期报错;d 的绑定推迟到运行时,按真实类型 int 执行加法,输出 11。

关键点: dynamic 与 object 的差别是绑定时机,不是运行时类型。

0784 以下代码输出什么?

难度: 进阶

class Program
{
    static void M(int x) => Console.WriteLine("int");
    static void M(long x) => Console.WriteLine("long");
    static void M(object x) => Console.WriteLine("object");

    static void Main()
    {
        dynamic d = 1;
        M(d);
    }
}
  • A. 编译失败,dynamic 不能参与重载决议
  • B. 输出 int,运行时按实参真实类型选择最合适重载
  • C. 输出 object
  • D. 输出 long
查看答案与解析

正确答案B

解析: dynamic 实参把重载决议推迟到运行时,运行时按实参真实类型 int,在所有候选重载中选择最具体的 M(int),因此输出 int。决议规则与编译期一致,只是时机不同。

关键点: dynamic 参与“运行时重载决议”,规则与编译期相同。

0785 以下代码输出什么?

难度: 实战

dynamic items = new List<int> { 1, 2, 3 };
Console.WriteLine(items.Sum());
  • A. 抛 RuntimeBinderException,dynamic 调用不解析扩展方法
  • B. 输出 6
  • C. 编译失败
  • D. 输出 0
查看答案与解析

正确答案A

解析: 扩展方法靠编译期 using 查找并静态解析;dynamic 接收者把所有绑定推迟到运行时,而运行时只搜索实例成员,List<int> 没有实例 Sum(),于是抛 RuntimeBinderException

关键点: dynamic 调用永远不会命中扩展方法。

0786 以下代码能否编译?

难度: 进阶

dynamic d = 1;
Expression<Func<int, int>> e = x => x + d;
  • A. 编译通过,d 在运行时求值
  • B. 编译通过,但 e 编译时抛异常
  • C. 编译失败,表达式树不能包含 dynamic 操作
  • D. 编译通过,d 被当作 object 装箱
查看答案与解析

正确答案C

解析: 表达式树只能用节点描述可静态分析的表达式,dynamic 操作的绑定无法用表达式树节点表达,编译器直接报“表达式树不能包含 dynamic 操作”。

关键点: 表达式树与 dynamic 互斥。

0787 以下代码能否编译?

难度: 进阶

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

正确答案B

解析: 表达式树只支持表达式体 lambda;带花括号的语句体 lambda 无法转换为表达式树,这是编译期规则。

关键点: 转表达式树的 lambda 必须是表达式体。

0788 以下代码输出什么?

难度: 进阶

Expression<Func<int, int>> e = x => x + 1;
var f1 = e.Compile();
var f2 = e.Compile();

Console.WriteLine(ReferenceEquals(f1, f2));
  • A. 编译失败
  • B. 输出 True,同一表达式 Compile 结果被缓存
  • C. 输出 True,委托是值类型
  • D. 输出 False,每次 Compile 都生成新委托
查看答案与解析

正确答案D

解析: Expression.Compile() 每次都会完整走一遍代码生成并返回新委托,框架不缓存编译结果。性能敏感代码必须自己缓存编译后的委托。

关键点: Compile 有成本且结果不缓存。

0789 以下代码输出什么?

难度: 实战

int y = 5;
Expression<Func<int, int>> e = x => x + y;
y = 10;

Console.WriteLine(e.Compile()(1));
  • A. 输出 11,表达式树按引用捕获变量,编译后读取当前值
  • B. 输出 6,创建表达式树时对 y 做了快照
  • C. 输出 5
  • D. 抛异常
查看答案与解析

正确答案A

解析: 表达式树与委托一样通过闭包字段按引用捕获外部变量,编译后访问的是闭包字段的当前值(y=10),因此输出 11;它不会在构建树时复制数值。

关键点: 表达式树捕获变量是“按引用”,不是按值快照。

0790 以下代码输出什么?

难度: 进阶

dynamic d = 3.14;
int i = d;

Console.WriteLine(i);
  • A. 编译失败,dynamic 不能隐式转换为 int
  • B. 运行抛 RuntimeBinderException,double 到 int 不存在隐式转换
  • C. 输出 3
  • D. 输出 3.14
查看答案与解析

正确答案B

解析: 从 dynamic 出发的转换在编译期一律放行(推迟到运行时判定);运行时 double→int 只有显式转换,隐式转换不成立,抛 RuntimeBinderException

关键点: dynamic 转换“能编译”不代表“能运行”。

0791 以下代码输出什么?

难度: 进阶

Expression<Func<int, int>> e = x => x * 2;

Console.WriteLine(e.NodeType);
Console.WriteLine(e.Body.NodeType);
  • A. LambdaAdd
  • B. LambdaExpressionBinary
  • C. LambdaMultiply
  • D. MultiplyLambda
查看答案与解析

正确答案C

解析: 根节点是 Lambda(NodeType 为 ExpressionType.Lambda),主体 x * 2 是乘法二元表达式,NodeType 为 Multiply

关键点: 表达式树是节点化的数据,NodeType 描述每个节点的语义。

0792 关于表达式树的 Compile 成本,哪项正确?

难度: 进阶

  • A. 同一 Expression 多次 Compile 自动复用委托,无需缓存
  • B. Compile 只是遍历语法树,成本可忽略
  • C. .NET Core 3.0+ 默认用解释器执行表达式树,不生成委托
  • D. 每次 Compile 都生成新委托,热点应缓存;.NET Core 3.0+ 已移除解释器,preferInterpretation 被忽略
查看答案与解析

正确答案D

解析: 每次 Compile 都生成新的委托,重复调用会重复付出代码生成成本;.NET Core 3.0 起表达式树解释器被移除,Compile(preferInterpretation: true) 也会编译为委托。高频路径必须缓存编译结果。

关键点: 缓存 Compile 结果是表达式树高性能使用的关键。

0793 以下代码中 r1 与 r2 分别绑定到哪个扩展方法?

难度: 实战

IQueryable<int> q = new List<int> { 1, 2, 3 }.AsQueryable();
IEnumerable<int> e = q;

var r1 = q.Where(x => x > 1);
var r2 = e.Where(x => x > 1);
  • A. r1 绑定 Queryable.Where(表达式树),r2 绑定 Enumerable.Where(委托),取决于变量的静态类型
  • B. 两者都绑定 Enumerable.Where
  • C. 两者都绑定 Queryable.Where
  • D. 编译失败,不能对同一对象同时使用两套扩展
查看答案与解析

正确答案A

解析: 扩展方法决议看变量的静态类型:q 是 IQueryable<int>,r1 绑定 Queryable.Where(构建表达式树);e 是 IEnumerable<int>,r2 绑定 Enumerable.Where(编译成委托)。底层对象相同不影响绑定结果。

关键点: Queryable 与 Enumerable 的选择由静态类型决定,与运行时对象无关。

0794 关于 IQueryable 与 LINQ 提供程序,哪项正确?

难度: 实战

  • A. IQueryable.Where 立即执行并返回结果集
  • B. 查询在构造 IQueryable 时就已执行
  • C. IQueryable 承载表达式树,由提供程序翻译后执行,查询延迟到枚举才执行,且不是所有方法都能翻译
  • D. 提供程序在编译期校验所有方法调用
查看答案与解析

正确答案C

解析: IQueryable 的核心是“表达式树 + 提供程序翻译 + 延迟执行”:Where 只往树里追加节点,枚举(foreach/ToList/Count 等)时才真正执行;能否翻译取决于提供程序,翻译不了的方法在执行期报错。

关键点: 表达式树与延迟执行是 LINQ 提供程序的两大支柱。

0795 以下代码输出什么?

难度: 实战

IQueryable<int> q = new List<int> { 1, 2, 3 }.AsQueryable();
q = q.Where(x => x > 1);
Console.WriteLine("构建完成");
Console.WriteLine(q.Count());
  • A. 输出 构建完成,然后抛 NotSupportedException
  • B. 输出 构建完成2,Where 只构建表达式树,Count() 才触发执行
  • C. 只输出 构建完成,Where 与 Count 都未执行
  • D. 输出 2构建完成
查看答案与解析

正确答案B

解析: Where 只是给表达式树追加节点,不触碰数据;Count() 触发执行并返回 2,因此先输出“构建完成”再输出 2。顺序本身也说明 Where 没有立即执行。

关键点: 构建查询不执行,枚举/聚合才执行。

0796 关于表达式树的数据结构特性,哪项正确?

难度: 进阶

  • A. 表达式树是不可变数据结构,需要改造时必须用 ExpressionVisitor 重建新树
  • B. 表达式树节点创建后可以原地修改
  • C. 表达式树等价于已编译的委托
  • D. 表达式树只能表示语句块
查看答案与解析

正确答案A

解析: 表达式树的所有节点创建后不可变,想要变换(如把某个节点换成另一个表达式)必须用 ExpressionVisitor 遍历并重建新树。

关键点: 不可变 + 可遍历是表达式树变换的基础。

0797 以下代码能否编译?

难度: 进阶

Expression<Func<Task<int>>> e = async () => 1;
  • A. 编译通过,运行时返回 1
  • B. 编译通过,返回 null
  • C. 编译通过,运行时抛 NotSupportedException
  • D. 编译失败,表达式树不能包含异步操作
查看答案与解析

正确答案D

解析: async lambda 会生成状态机,无法用表达式树节点表达,编译器报“表达式树不能包含异步操作”。

关键点: async 与表达式树不兼容。

0798 以下代码输出什么?

难度: 实战

dynamic obj = new ExpandoObject();
obj.Name = "Tom";

Console.WriteLine(obj.Name);
  • A. 编译失败
  • B. 抛 RuntimeBinderException,ExpandoObject 不支持动态添加成员
  • C. 输出 Tom,ExpandoObject 通过 IDynamicMetaObjectProvider 接受动态成员
  • D. 输出空行
查看答案与解析

正确答案C

解析: ExpandoObject 实现 IDynamicMetaObjectProvider,运行时成员的新增、读取与删除都由它接管;动态绑定给 obj 添加 Name 属性后,读取输出 Tom。

关键点: IDynamicMetaObjectProvider 让对象可以在运行时拥有可变成员集合。

0799 以下代码输出什么?

难度: 实战

var param = Expression.Parameter(typeof(C), "c");
var getter = Expression.Lambda<Func<C, int>>(
    Expression.Property(param, nameof(C.Value)), param).Compile();

var c = new C { Value = 7 };
Console.WriteLine(getter(c));

class C { public int Value { get; set; } }
  • A. 输出 0
  • B. 输出 7,表达式树把属性访问编译成强类型委托
  • C. 抛 MissingMethodException
  • D. 编译失败
查看答案与解析

正确答案B

解析: 表达式树把属性读取编译为强类型委托,调用 getter 相当于直接调用属性 getter,输出 7。这是用表达式树替代反射 GetValue、避免反射开销的典型用法。

关键点: Compile 出的委托是强类型、可直接调用的。

0800 以下代码输出什么?

难度: 实战

dynamic d = new[] { 1, 2, 3 };
Console.WriteLine(d[1]);
  • A. 输出 2,索引器访问也参与动态绑定
  • B. 编译失败,dynamic 不支持索引器
  • C. 抛 RuntimeBinderException
  • D. 输出 1
查看答案与解析

正确答案A

解析: 索引器访问是完整的动态绑定操作:运行时按真实类型 int[] 解析索引器并取值,输出 2。

关键点: dynamic 支持索引器、运算、转换等全套绑定操作。

官方资料

当前分类

C# 选择题

查看全部分类 →
  1. 38C# 试题 38:GC 与分配20 题
  2. 39C# 试题 39:反射与特性20 题
  3. 40C# 试题 40:dynamic 与表达式树20 题
  4. 41C# 试题 41:System.Text.Json20 题
  5. 42C# 试题 42:HttpClient 与网络基础20 题
ESC

输入关键词开始搜索