前言

这部分本来应该和泛型一起讨论,考虑到分类问题,遂单独拿出来记录。

default运算符

早在C# 1时代,typeof()运算符就已经出现了,它接收一个类型名称作为唯一操作数。C# 2加入了default()运算符,并且略微拓展了typeof()的用途。

default运算符功能比较简单,它是一元运算符,其操作数是类型名或者类型形参,返回值是该类型的默认值。当声明一个字段,但是没有为该字段立刻赋值时,该字段的值就是默认值。如果是引用类型,默认值是一个null引用。如果是非可空值类型,将返回对应类型的”0值”(0,0.0,0.0m,false,UTF-16编码单元的0等);如果是可空值类型则返回类型的null值。

default运算符可以用于类型形参以及提供了类型实参的泛型类型,例如如下都是合法的:

1
2
3
4
default(T)
default(int)
default(string)
default(List<T>)

default运算符返回值的类型与操作数类型一致。**default常与泛型类型形参一起使用**,因为对于非泛型类型可以通过其他方式获取default值。但是对于泛型类型来说,我们不确定该泛型的初始值是什么类型的,这个时候就需要使用default运算符了,代码示例:

1
2
3
4
5
6
7
public T Last<T> (IEnumerable<T> source){
T ret = default(T);
foreach(T item in source){
ret = item;
}
return ret;
}

typeof运算符

typeof运算符的使用相对复杂一些,考虑到如下几种常见的情形:

  • 不涉及泛型,例如:typeof(string)
  • 涉及泛型但是不涉及类型形参,例如:typeof(List<int>)
  • 仅涉及类型形参,例如:typeof(T)
  • typeof操作数中有泛型,而且泛型作为类型形参出现,例如:typeof(List<TItem>)
  • 涉及泛型,但是操作数中没有出现类型实参,例如:typeof(List<>)

其中第一个场景最简单,且其用法基本没有变过。对于其他的场景需要进行考虑。typeof运算符的返回值是Type类型的值,而且Type类在经过拓展之后可以支持泛型。

要理解typeof运算符,一个简单的例子是**typeof(List<int>),其返回值是List<int>Type值,结果与调用new List<int>().GetType()相同**。

现在来讨论typeof(T),该表达式返回的是调用代码中T类型参数的Type。它的返回值永远是一个封闭的,已构造的类型,技术规范中将其描述为一个真正不包含任何类型形参的类型。

下面通过一个实例来展示typeof(T)typeof(List<T>),代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//程序入口
static void Main()
{
PrintType<int>();
Console.WriteLine("=================");
PrintType<string>();
Console.ReadKey();
}

//自定义方法
static void PrintType<T>()
{
Console.WriteLine("typeof(T)= {0}", typeof(T));
Console.WriteLine("typeof(T)= {0}", typeof(List<T>));
}

运行结果:

image-20220625234056672

结果一幕了然,其中还展示了使用反射时泛型类型的命名格式:List '1(注是斜上点,Markdown字符冲突)',表示这是一个命名为List的泛型类型,其度为 1 ,后面的方括号表示类型实参。

最后再说明一下typeof(List<>)。该表达式看起来缺少类型实参。这种写法只有在typeof运算符中才有效,且指向了泛型类型定义。对于度为 1 的泛型,其书写格式为TypeName<>,**如果其度大于1,每增加一个度,就加一个逗号,例如要获取D<T1,T2>泛型类型定义,则表达式为: Typeof(TypeName<,>)**。

End

说实话,这两种运算符,在实际应用过程并没有那么的应用,是一种数据获取手段,此处单独摘录说明,看看我接下来有没有时间写一下C#多线程的内容。

就这样,祝晚安。