C# 试题 15:泛型基础
0281 以下代码运行结果是什么?
难度: 实战
var ints = new List<int> { 1, 2, 3 };
object box = ints;
var objs = (List<object>)box;
Console.WriteLine(objs.Count);
- A. 编译失败,
List<int>不能赋给object - B. 输出
3 - C. 运行抛
InvalidCastException,List<int>与List<object>是运行期不同类型的实例 - D. 运行抛
ArrayTypeMismatchException
查看答案与解析
正确答案C
解析: .NET 泛型是 reified(类型参数保留到运行期),List<T> 本身不变;强转检查的是对象实际类型 List<int>,它不是 List<object> 的实例,抛 InvalidCastException。这与 Java 泛型擦除后的“强转普遍可过”完全不同。
关键点: List<int> 与 List<object> 是两个不同的运行期类型。
0282 以下代码输出什么?
难度: 进阶
var list = new List<int> { 7 };
int v = list[0];
Console.WriteLine(v);
- A. 编译失败
- B. 输出
7,但Add与索引读取都会装箱 - C. 运行抛
InvalidCastException - D. 输出
7,存取过程不涉及装箱
查看答案与解析
正确答案D
解析: List<int> 以 int 专门化,元素直接以内联值存储,Add/索引器都不装箱;只有把 int 赋给 object 之类的引用位置才装箱。泛型消除值类型进出的装箱是其核心收益之一。
关键点: 值类型实参的泛型容器不会对元素装箱。
0283 以下代码输出什么?
难度: 进阶
var s = default(string);
var n = default(int?);
Console.WriteLine(s == null);
Console.WriteLine(n == null);
Console.WriteLine(n.HasValue);
- A.
False、False、False - B.
True、True、True - C.
True、True、False - D. 编译失败
查看答案与解析
正确答案C
解析: default(string) 是 null;default(int?) 是”无值”的 Nullable<int>,与 null 比较为 True,HasValue 为 False。两者都表示”空”,但底层完全不同:前者是无引用的引用类型,后者是装箱过的结构体。
关键点: 引用类型与 Nullable<T> 的 default 都是”空”,但机制不同。
0284 以下代码能否编译?
难度: 进阶
var x = default;
Console.WriteLine(x);
- A. 编译失败,
default字面量无法推断目标类型 - B. 编译通过,输出
0 - C. 编译通过,输出
null - D. 编译通过,运行抛
NullReferenceException
查看答案与解析
正确答案A
解析: C# 7.1 引入的 default 字面量必须存在可推断的目标类型;var 不提供任何目标类型,编译器无法确定是 int、string 还是别的,直接报错。这与 int x = default;(合法)形成对比。
关键点: 无目标类型的 default 无法编译,var 救不了它。
0285 以下代码输出什么?
难度: 进阶
static string Describe<T>() => default(T) is null ? "null" : "non-null";
Console.WriteLine(Describe<int>());
Console.WriteLine(Describe<string>());
- A.
null、null - B.
non-null、null - C.
null、non-null - D. 两行都抛异常
查看答案与解析
正确答案B
解析: default(int) 是 0,与 null 比较需要装箱成 0,不是 null;default(string) 才是 null。资深开发者常把 default(T) 一律当成 null,实际上对值类型它是零值。
关键点: default(T) 对值类型是零值而非 null。
0286 以下代码输出什么?
难度: 实战
Type open = typeof(List<>);
Console.WriteLine(open.IsGenericType);
Console.WriteLine(open.IsGenericTypeDefinition);
Console.WriteLine(open.GetGenericArguments()[0].IsGenericParameter);
- A.
True、True、True - B.
True、True、False - C.
True、False、True - D. 第二行抛异常
查看答案与解析
正确答案A
解析: IsGenericType 对泛型类型定义和构造类型都返回 True,只有 IsGenericTypeDefinition 能区分 List<>(定义)与 List<int>(封闭);定义上的类型实参是 IsGenericParameter 为 True 的泛型参数 T,而不是具体类型。
关键点: “开放泛型”与”泛型定义”是两个不同概念,别用 IsGenericType 判断是否已封闭。
0287 以下代码输出什么?
难度: 进阶
Type open = typeof(List<>);
Type closed = open.MakeGenericType(typeof(int));
Console.WriteLine(closed == typeof(List<int>));
Console.WriteLine(open.GetGenericArguments()[0].Name);
- A.
False、T - B.
True、Int32 - C. 运行抛异常
- D.
True、T
查看答案与解析
正确答案D
解析: MakeGenericType(typeof(int)) 把开放类型构造为 List<int>,与 typeof(List<int>) 是同一个 Type 对象;定义上的第一个类型参数名是 T(来自泛型定义,不是 Int32)。
关键点: 反射实例化泛型用 MakeGenericType,参数名仍保留定义里的名字。
0288 以下代码运行结果是什么?
难度: 进阶
Type t = typeof(List<int>);
t.MakeGenericType(typeof(string));
- A. 编译失败
- B. 返回
List<string> - C. 运行抛
InvalidOperationException,已构造类型不能再MakeGenericType - D. 运行抛
ArgumentException
查看答案与解析
正确答案C
解析: MakeGenericType 只接受泛型类型定义(IsGenericTypeDefinition == true)。List<int> 已封闭,再调用抛 InvalidOperationException。若想得到 List<string>,必须拿 typeof(List<>) 来构造。
关键点: 反射构造类型前先确认拿的是定义而不是已封闭类型。
0289 以下代码输出什么?
难度: 实战
class Counter<T>
{
public static int Count;
}
Counter<int>.Count++;
Counter<string>.Count++;
Counter<int>.Count++;
Console.WriteLine(Counter<int>.Count);
Console.WriteLine(Counter<string>.Count);
- A.
2、2 - B.
2、1 - C.
1、1 - D. 编译失败
查看答案与解析
正确答案B
解析: CLR 为每个封闭泛型类型单独分配静态存储:Counter<int> 与 Counter<string> 各有一份 Count。Counter<int>.Count 自增两次为 2,Counter<string>.Count 自增一次为 1。
关键点: 泛型静态字段按封闭类型隔离,“全局计数”很容易踩坑。
0290 以下代码输出什么?
难度: 进阶
class Holder<T>
{
public static int Next;
public static int Id = ++Next;
}
Console.WriteLine(Holder<int>.Id);
Console.WriteLine(Holder<int>.Id);
Console.WriteLine(Holder<string>.Id);
- A.
1、1、1 - B.
1、2、1 - C.
1、2、3 - D. 编译失败
查看答案与解析
正确答案A
解析: 静态初始化每个封闭类型只执行一次:Holder<int> 的 Id 初始化后恒为 1;Holder<string> 是另一份独立的静态状态,其 Next 也从 0 开始,Id 同样为 1。三个输出都是 1。
关键点: 静态初始化按封闭类型执行一次,别按”共享状态”推导。
0291 关于泛型类型中的静态字段,哪项正确?
难度: 进阶
- A. 所有泛型实例共享同一份静态字段,与类型实参无关
- B. 每个封闭泛型类型各有一份独立静态字段,
Counter<int>与Counter<string>互不影响 - C. 静态字段会随类型实参装箱
- D. 泛型类型不能声明静态字段
查看答案与解析
正确答案B
解析: 泛型类型是”类型模板”,每次用不同类型实参封闭都产生独立的类型,静态字段随之独立。跨封闭类型共享状态需要显式用 static 非泛型辅助类或 ConcurrentDictionary<Type, ...> 之类的设计。
关键点: 泛型静态字段 = 按封闭类型隔离,不跨类型共享。
0292 以下代码能否编译?
难度: 实战
static T M<T>(T a, T b) => a;
var r = M(1, 2L);
- A. 编译失败,
int与long之间无法推断出单一T - B. 编译通过,
T推断为long - C. 编译通过,
T推断为object - D. 编译通过,
T推断为int,2L被截断
查看答案与解析
正确答案A
解析: 泛型方法类型推断不做”最佳公共类型”合并:int 与 long 之间没有可同时满足 T 的隐式转换(提升不会发生在推断阶段),报 CS0411。必须显式写类型实参或让实参类型一致。
关键点: 推断不做数值提升/合并,M(1, 2L) 是经典编译失败。
0293 以下代码输出什么?
难度: 进阶
static T M<T>(T a, T b) => a;
Console.WriteLine(M("a", null));
- A. 编译失败
- B. 输出空串
- C. 运行抛
NullReferenceException - D. 输出
a,T从"a"推断为string,null作为string实参合法
查看答案与解析
正确答案D
解析: 推断先从 "a" 得到 T = string,随后 null 字面量按 string 转换即可,编译通过并输出 a。null 本身不能独立推断类型,但可以配合其它实参完成推断。
关键点: null 字面量无法单独提供类型信息,但有其它实参时会被”套用”。
0294 以下代码能否编译?
难度: 实战
static T M<T>(T x) => x;
var s = M(null);
- A. 编译失败,无法从
null推断T - B. 编译通过,
T推断为string - C. 编译通过,
T推断为object - D. 编译通过,运行抛
ArgumentNullException
查看答案与解析
正确答案A
解析: null 字面量没有任何类型信息,单独作为实参时推断失败(CS0411)。必须显式指定,如 M<string?>(null)。这与上题的”"a" 提供类型后 null 可套用”形成对照。
关键点: 单靠 null 实参无法完成泛型推断。
0295 关于泛型方法类型推断,哪项正确?
难度: 进阶
- A. 编译器会根据返回值被赋给的变量类型推断
T - B. 推断基于实参与上下文在编译期进行,返回类型通常不参与推断
- C. 推断发生在运行时
- D. 推断只适用于值类型实参
查看答案与解析
正确答案B
解析: 泛型推断只从实参及调用上下文推导类型参数,返回值位置通常不作为推断来源;所以 static T M<T>() => default; 不能靠 var x = M(); 推断。推断是编译期行为,与运行时无关。
关键点: “按返回值推断泛型”是错误直觉,返回类型一般不参与推断。
0296 以下代码能否编译?
难度: 实战
static T M<T>(T x) => x;
Console.WriteLine(M<int>(1.5));
- A. 编译通过,输出
1 - B. 编译通过,输出
1.5 - C. 编译失败,
double不能隐式转换为int - D. 编译通过,运行抛
InvalidCastException
查看答案与解析
正确答案C
解析: 显式指定类型实参后参数类型固定为 int,1.5 是 double,二者没有隐式转换(数值转换里也没有 double→int 的隐式方向),编译失败。显式类型实参取消推断,但普通重载决议与转换规则照常生效。
关键点: 显式类型实参不等于”帮忙转换”,类型不符照样编译失败。
0297 以下代码输出什么?
难度: 实战
IEnumerable<object> e = new List<string> { "ok" };
foreach (var item in e)
{
Console.WriteLine(item);
}
- A. 编译失败,
List<string>不能转换为IEnumerable<object> - B. 编译通过,输出
ok,IEnumerable<out T>协变允许引用类型转换 - C. 编译通过,运行抛
InvalidCastException - D. 编译通过,什么都不输出
查看答案与解析
正确答案B
解析: 转换链是 List<string> → IEnumerable<string> →(协变)IEnumerable<object>,引用类型之间全部合法,运行期无异常。协变只存在于只读接口/委托等”只出不进”的位置,List<T> 本身不变。
关键点: 协变是接口/委托层面的转换,List<object> = List<string> 依然不合法。
0298 关于开放泛型类型(List<>)与封闭泛型类型(List<int>),哪项正确?
难度: 进阶
- A. 开放类型含未绑定类型参数,不能直接实例化;封闭类型参数全部绑定,可正常使用
- B. 两者在运行时是同一个
Type对象 - C. 开放类型可以直接
new出实例 - D. 封闭类型不能用于反射
查看答案与解析
正确答案A
解析: List<> 是泛型类型定义,其中 T 未绑定,不能 new List<>(),只能用于反射构造;List<int> 全部绑定,可直接实例化。开放类型与封闭类型是运行时不同的 Type 对象,ContainsGenericParameters 可区分二者。
关键点: 开放类型不可实例化,只能作为类型定义存在。
0299 以下代码输出什么?
难度: 实战
static string TypeName<T>() => typeof(T).Name;
Console.WriteLine(TypeName<int>());
- A.
T - B.
Int32 - C.
System.Int32 - D. 编译失败
查看答案与解析
正确答案B
解析: .NET 泛型是 reified:运行期 typeof(T) 拿到的是真实类型 System.Int32,其 Name 是 Int32(FullName 才是 System.Int32)。这与 Java 的泛型擦除(运行期只剩 Object)本质不同。
关键点: 泛型类型参数在运行期保留,typeof(T) 返回实际类型。
0300 以下代码输出什么?
难度: 进阶
static void M<T>(Func<T> f) => Console.WriteLine(typeof(T).Name);
M(() => 42);
- A. 编译失败,lambda 不参与类型推断
- B. 编译通过,输出
Object - C. 编译通过,输出
Int32,编译器从 lambda 返回类型推断T - D. 编译通过,输出
T
查看答案与解析
正确答案C
解析: C# 10 起,无显式返回类型的 lambda 其返回类型可参与泛型方法推断:() => 42 的返回类型是 int,于是 T = int,输出 Int32。这是”返回类型通常不参与推断”的少数例外,推断来源是 lambda 体。
关键点: 表达式体 lambda 的返回类型可以作为推断来源。