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

C# 试题 39:反射与特性

0761 以下代码输出什么?

难度: 实战

[AttributeUsage(AttributeTargets.Class)]
class Marker : Attribute
{
    public Marker() => Console.WriteLine("ctor");
}

[Marker]
class Foo { }

Console.WriteLine("before");
var attr = typeof(Foo).GetCustomAttribute<Marker>();
Console.WriteLine("after");
  • A. before\nafter:特性不会实例化
  • B. ctor\nbefore\nafter
  • C. before\nctor\nafter:GetCustomAttribute 会实例化特性对象,构造函数在读取时执行
  • D. 编译失败:特性构造函数不能有副作用
查看答案与解析

正确答案C

解析: 特性的参数写入元数据,但 GetCustomAttribute 会按元数据实例化特性对象,因此其构造函数在读取时真正执行。若只想读参数而不实例化,应使用 CustomAttributeData

关键点: 读取特性实例会执行构造函数,这不是“纯元数据”操作。

0762 以下代码输出什么?

难度: 实战

[AttributeUsage(AttributeTargets.Class)]
class Marker : Attribute
{
    public int Value { get; }
    public Marker(int v) => Value = v;
}

[Marker(42)]
class Foo { }

var data = typeof(Foo).GetCustomAttributesData().Single();
Console.WriteLine(data.ConstructorArguments[0].Value);
  • A. 抛 InvalidOperationException:参数不在元数据中
  • B. 42:CustomAttributeData 直接读取元数据,不实例化特性
  • C. 输出 Marker
  • D. 编译失败:CustomAttributeData 不存在
查看答案与解析

正确答案B

解析: CustomAttributeData 只解析元数据中的构造参数与具名参数,不构造特性实例,因此不会执行特性构造函数。ConstructorArguments[0].Value 就是元数据里的 42。

关键点: 只读元数据用 CustomAttributeData,避免实例化副作用。

0763 关于特性在元数据中的表示,哪项正确?

难度: 进阶

  • A. 特性以构造参数和具名参数的形式写入元数据;GetCustomAttribute 实例化特性时会执行其构造函数,用 CustomAttributeData 可只读元数据而不实例化
  • B. 特性保存为 IL 指令,读取时总是执行
  • C. 特性不能有构造参数
  • D. 特性只存在于编译期,运行期不可见
查看答案与解析

正确答案A

解析: 特性实例化发生在运行时读取之时:GetCustomAttribute/GetCustomAttributes 根据元数据调用构造函数创建实例,构造函数代码会运行。CustomAttributeData 则停留在元数据层,两者语义不同。

关键点: 特性 = 元数据 + 可执行的构造函数,读取方式决定是否执行。

0764 以下代码输出什么?

难度: 进阶

[AttributeUsage(AttributeTargets.Class, Inherited = true)]
class BaseAttr : Attribute { }

[BaseAttr]
class Base { }

class Derived : Base { }

Console.WriteLine(typeof(Derived).IsDefined(typeof(BaseAttr), inherit: true));
  • A. False
  • B. 抛 ArgumentException
  • C. BaseAttr
  • D. True:Inherited=true 时派生类型继承基类型特性,inherit 参数控制是否沿继承链查找
查看答案与解析

正确答案D

解析: Inherited=true 时特性沿继承链传播,Derived 虽然没有直接标注,也“拥有”基类的 BaseAttrIsDefined(..., inherit: true) 会沿继承链查找,因此返回 true;若传 false 则只查本类型。

关键点: 特性继承由 AttributeUsage.Inherited 与查询的 inherit 参数共同决定。

0765 以下代码输出什么?

难度: 实战

class Calc
{
    public int Div(int a, int b) => a / b;
}

var m = typeof(Calc).GetMethod("Div");

try
{
    m.Invoke(new Calc(), new object[] { 10, 0 });
}
catch (Exception ex)
{
    Console.WriteLine(ex.GetType().Name);
}
  • A. DivideByZeroException
  • B. TargetInvocationException:被反射调用的方法抛出的异常会被包装进 TargetInvocationException
  • C. ArgumentException
  • D. InvalidOperationException
查看答案与解析

正确答案B

解析: MethodInfo.Invoke 执行目标方法时,方法内部抛出的任何异常都会被包装进 TargetInvocationException,原始异常在 InnerException 中。需要解包 InnerException 才能看到真正的 DivideByZeroException

关键点: Invoke 把被调方法的异常包成 TargetInvocationException。

0766 以下代码输出什么?

难度: 实战

class StaticHelper
{
    public static string Hello() => "hi";
}

var m = typeof(StaticHelper).GetMethod("Hello");
var result = m.Invoke(null, null);

Console.WriteLine(result);
  • A. 抛 ArgumentNullException:obj 不能为 null
  • B. 抛 TargetInvocationException
  • C. hi:静态方法 Invoke 时实例参数传 null 即可
  • D. 编译失败:Invoke 需要实例
查看答案与解析

正确答案C

解析: 调用静态方法时,Invoke 的实例参数必须为 null(传非 null 反而抛 ArgumentException);无参方法实参数组也可传 null。因此正常返回 “hi”。

关键点: 静态方法 Invoke 的 obj 传 null,实参数组可为 null。

0767 以下代码输出什么?

难度: 实战

class Secret
{
    private int Hidden() => 42;
}

var m = typeof(Secret).GetMethod("Hidden");
Console.WriteLine(m is null);
  • A. False:默认也能找到私有方法
  • B. True:GetMethod 默认只查找公共方法,私有方法需要 BindingFlags.NonPublic
  • C. 抛 MethodAccessException
  • D. 42
查看答案与解析

正确答案B

解析: GetMethod(name) 默认使用 Public | Instance | Static 标志,私有成员不在其中,返回 null。要获取私有方法必须显式传 BindingFlags.NonPublic | BindingFlags.Instance 等组合。

关键点: 默认标志不含 NonPublic,私有成员需要显式 BindingFlags。

0768 以下代码输出什么?

难度: 进阶

var m = typeof(string).GetMethod("Substring", new[] { typeof(int) });

try
{
    m.Invoke("hello", new object[] { "1" });
}
catch (Exception ex)
{
    Console.WriteLine(ex.GetType().Name);
}
  • A. ArgumentException:参数类型不匹配,Invoke 在绑定阶段抛 ArgumentException
  • B. FormatException
  • C. TargetInvocationException
  • D. InvalidCastException
查看答案与解析

正确答案A

解析: Invoke 先做参数绑定:实参 "1" 是 string,无法转换为形参 int,绑定失败抛 ArgumentException(与实参个数不符抛 TargetParameterCountException)。方法尚未执行,因此不是 TargetInvocationException。

关键点: 参数不匹配在绑定阶段抛 ArgumentException,方法体未运行。

0769 以下代码输出什么?

难度: 实战

var open = typeof(List<>);
Console.WriteLine(open.IsGenericTypeDefinition);

var closed = open.MakeGenericType(typeof(int));
Console.WriteLine(closed == typeof(List<int>));
  • A. True\nFalse
  • B. True\nTrue:MakeGenericType 用类型实参构造封闭泛型,与 typeof(List<int>) 是同一类型
  • C. False\nFalse
  • D. 抛 ArgumentException
查看答案与解析

正确答案B

解析: typeof(List<>) 是开放泛型定义(IsGenericTypeDefinition 为 true)。MakeGenericType(typeof(int)) 生成封闭泛型,与 typeof(List<int>) 是同一个运行时类型。开放泛型不能直接实例化。

关键点: MakeGenericType 闭合开放泛型,结果与 typeof 构造一致。

0770 以下代码输出什么?

难度: 实战

try
{
    Activator.CreateInstance(typeof(List<>));
}
catch (Exception ex)
{
    Console.WriteLine(ex.GetType().Name);
}
  • A. MissingMethodException
  • B. InvalidOperationException
  • C. 正常运行
  • D. ArgumentException:开放泛型类型(IsGenericTypeDefinition)不能直接实例化,必须先 MakeGenericType 闭合
查看答案与解析

正确答案D

解析: typeof(List<>) 含泛型占位参数,无法实例化,Activator.CreateInstanceArgumentException(提示 ContainsGenericParameters 为 true)。必须先 MakeGenericType(typeof(int)) 得到封闭类型再创建实例。

关键点: 开放泛型不能实例化,闭合并选择正确构造函数是反射的常见坑。

0771 以下代码输出什么?

难度: 实战

class Util
{
    public static T Echo<T>(T value) => value;
}

var m = typeof(Util).GetMethod("Echo");
var closed = m.MakeGenericMethod(typeof(int));

var result = closed.Invoke(null, new object[] { 7 });
Console.WriteLine(result);
  • A. 抛 TargetInvocationException
  • B. 编译失败:泛型方法不能反射调用
  • C. 7:MakeGenericMethod 闭合泛型方法定义后再 Invoke
  • D. 抛 ArgumentException:泛型方法定义不能 Invoke
查看答案与解析

正确答案C

解析: 泛型方法定义(未指定类型实参)不能直接 Invoke;MakeGenericMethod(typeof(int)) 得到闭合版本,之后正常调用,返回装箱的 int 7,输出 7。

关键点: 泛型方法反射必须先 MakeGenericMethod 闭合。

0772 关于泛型反射,哪项正确?

难度: 进阶

  • A. typeof(List<>) 与 typeof(List<int>) 是同一个 Type 实例
  • B. 开放泛型(IsGenericTypeDefinition)不能直接实例化或 Invoke,必须先 MakeGenericType/MakeGenericMethod 闭合;GetGenericArguments 返回占位参数而非具体类型
  • C. GetGenericArguments 在开放泛型上一定为空
  • D. 泛型方法不需要 MakeGenericMethod 就能直接 Invoke
查看答案与解析

正确答案B

解析: 开放泛型定义的类型形参是占位符(IsGenericParameter 为 true),不是具体类型,因此不能实例化、不能 Invoke。必须先 MakeGenericType/MakeGenericMethod 提供实参;typeof(List<>)typeof(List<int>) 是两个不同 Type。

关键点: 区分开放泛型定义与封闭泛型是反射泛型的核心。

0773 以下代码输出什么?

难度: 实战

class Foo
{
    public int A { get; set; }
    private int B { get; set; }
}

var props = typeof(Foo).GetProperties();
Console.WriteLine(props.Length);
  • A. 2
  • B. 1:默认只返回公共属性(含继承的公共成员),私有属性需要 BindingFlags.NonPublic
  • C. 0
  • D. 抛 AmbiguousMatchException
查看答案与解析

正确答案B

解析: GetProperties() 默认标志只包含公共实例与公共静态属性,私有属性 B 被排除,因此返回 1。要枚举私有属性必须传 BindingFlags.NonPublic | BindingFlags.Instance

关键点: 默认反射枚举不含私有成员。

0774 以下代码输出什么?

难度: 进阶

class Base { public int X() => 1; }
class Derived : Base { public new int X() => 2; }

var m = typeof(Derived).GetMethod("X");
Console.WriteLine(m.DeclaringType?.Name);
  • A. Derived:GetMethod 返回派生类隐藏(new)后的最派生成员
  • B. Base
  • C. 抛 AmbiguousMatchException
  • D. 输出空
查看答案与解析

正确答案A

解析: new 隐藏(hide)基类同名成员后,GetMethod("X") 在派生类上找到的是 Derived 自己的声明,DeclaringType 为 Derived。反射沿“先派生后基类”的顺序查找,同名隐藏不会抛 AmbiguousMatchException。

关键点: 成员隐藏后反射返回最派生声明,DeclaringType 随之变化。

0775 以下代码输出什么?

难度: 实战

class Box
{
    private int _value = 5;
}

var f = typeof(Box).GetField("_value",
    BindingFlags.NonPublic | BindingFlags.Instance);

Console.WriteLine(f?.GetValue(new Box()));
  • A. null:反射不能访问私有字段
  • B. 抛 FieldAccessException
  • C. 编译失败:BindingFlags 未定义
  • D. 5:私有实例字段需要 NonPublic | Instance 标志,反射可在完全信任下读写
查看答案与解析

正确答案D

解析: 指定 BindingFlags.NonPublic | BindingFlags.Instance 后能拿到私有实例字段,GetValue 返回 5。反射对私有成员的访问受安全策略约束,普通 .NET 应用(完全信任)允许。

关键点: 私有成员反射需要 NonPublic 标志,读值用 GetValue。

0776 以下代码输出什么?

难度: 实战

class Counter
{
    public static int Total = 10;
}

var f = typeof(Counter).GetField("Total");
f.SetValue(null, 99);

Console.WriteLine(Counter.Total);
  • A. 10
  • B. 99:GetField 默认标志包含公共静态字段,SetValue 对静态字段传 null 实例
  • C. 抛 ArgumentException:静态字段不能反射赋值
  • D. 抛 TargetException
查看答案与解析

正确答案B

解析: 默认标志(Public | Instance | Static)能找到公共静态字段;静态字段没有实例,SetValue 的对象参数传 null。赋值后 Counter.Total 为 99。

关键点: 静态成员反射的实例参数为 null,默认标志含 Static。

0777 关于裁剪(trimming),以下代码哪项正确?

难度: 进阶

[RequiresUnreferencedCode("动态类型名反射")]
static object Create(string typeName)
{
    var type = Type.GetType(typeName);
    return Activator.CreateInstance(type)!;
}
  • A. 发布裁剪后,按类型名字符串动态反射的目标成员可能被裁掉,运行期抛异常或返回 null;应使用 [DynamicallyAccessedMembers]、源生成或显式保留根
  • B. 裁剪不影响反射
  • C. RequiresUnreferencedCode 只是注释,没有任何作用
  • D. NativeAOT 完全支持动态反射,无需处理
查看答案与解析

正确答案A

解析: 裁剪器从静态可分析的根出发,删掉“未被引用”的成员;按字符串动态查找类型/成员在裁剪分析中不可见,目标可能被裁掉,运行期反射失败。[RequiresUnreferencedCode] 向调用方告警,配合 [DynamicallyAccessedMembers]、源生成或显式保留根才能保证安全。

关键点: 动态字符串反射与裁剪不兼容,需要注解或保留根。

0778 关于验证方式,以下代码哪项正确?

难度: 进阶

static T Create<T>(string typeName) where T : class
{
    var type = Type.GetType(typeName);
    return (T)Activator.CreateInstance(type);
}
  • A. 单元测试通过即可证明裁剪部署安全
  • B. 编译期会自动报错
  • C. 仅 JIT 环境测试通过,不能证明裁剪部署安全;裁剪相关的反射失败通常只在发布裁剪后暴露
  • D. 裁剪只影响冷启动时间
查看答案与解析

正确答案C

解析: 普通开发/测试运行的是 JIT 未裁剪产物,所有成员都在,反射自然成功;发布时启用裁剪(PublishTrimmed)后成员才可能消失。因此必须在启用裁剪的发布产物上验证动态反射路径。

关键点: JIT 测试通过 ≠ 裁剪部署安全,验证要对着发布产物。

0779 以下代码哪项正确?

难度: 进阶

static void Scan(
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
    Type type)
{
    foreach (var m in type.GetMethods())
        Console.WriteLine(m.Name);
}
  • A. 注解是给裁剪分析器的契约:调用方必须保证传入类型保留 PublicMethods 成员,否则裁剪分析会报错
  • B. 注解没有任何效果
  • C. 注解必须在运行期手动检查
  • D. 编译失败:DynamicallyAccessedMembers 不能用于参数
查看答案与解析

正确答案A

解析: [DynamicallyAccessedMembers] 是裁剪分析器的契约注解:声明“此参数引用的类型必须保留 PublicMethods 等成员”,调用方如果传入无法满足的类型(如仅按字符串得到的 Type),裁剪分析会给出告警/错误。它把反射需求变成可静态分析的信息。

关键点: DynamicallyAccessedMembers 让动态成员需求对裁剪器可见。

0780 关于 NativeAOT / 裁剪与反射,哪项正确?

难度: 进阶

  • A. NativeAOT 支持全部反射 API,无需改动
  • B. NativeAOT 或裁剪下,动态反射可能因成员被裁掉而失效;应使用注解、源生成或明确根保留,并针对发布产物验证
  • C. 裁剪只在 Debug 配置生效
  • D. 反射引用的成员会自动全部保留
查看答案与解析

正确答案B

解析: NativeAOT 与裁剪都基于“静态可达性”精简程序,动态反射的成员不在静态图内就可能被裁掉。应对手段是注解、源生成(如 System.Text.Json 的 JsonSerializerContext)、保留根,并在发布产物上验证,而不是假设反射万能。

关键点: 裁剪时代反射不再万能,静态可分析的替代方案才是正解。

官方资料

当前分类

C# 选择题

查看全部分类 →
  1. 37C# 试题 37:并行、PLINQ 与 Channel20 题
  2. 38C# 试题 38:GC 与分配20 题
  3. 39C# 试题 39:反射与特性20 题
  4. 40C# 试题 40:dynamic 与表达式树20 题
  5. 41C# 试题 41:System.Text.Json20 题
ESC

输入关键词开始搜索