C # 语言有两种类型:值类型 和 引用类型。值类型的变量直接存储数据,而引用类型的变量则存储对数据(称为“对象”)的引用。
System.Object类(别名object
) 是所有类型的最终基类。值类型 派生自 System.ValueType类 (派生自 System.Object类)。派生自 System.ValueType类 的类型在 CLR 中具有特殊行为。引用类型 直接派生自 System.Object类。
不可变类型(Immutable):值类型或引用类型的数据被初始化赋值后是不可变的,如果被修改需在新内存地址保存新值,旧值所在的旧内存地址随后会被垃圾回收。
可变类型(Mutable):值类型或引用类型的数据被初始化赋值后是可变的,如果被修改只需在原有内存地址将新值取代旧值。
值类型包括:结构(可变)、整数(不可变)、十进制浮点(不可变)、IEEE二进制浮点(不可变)、布尔(不可变)、Unicode 字符(不可变)、枚举(不可变)、元组值类型(可变)、Nullable<T>
(不可变)。
引用类型包括:类(可变)、接口(可变)、Unicode 字符串(不可变)、数组(可变)、元组引用类型(不可变)、委托(不可变)。

1.结构(Struct)类型
别名 | 说明 | 默认值 | .NET API |
---|---|---|---|
struct | 格式为 struct S {...} 的用户自定义类型 | —————— | —————— |
2.整数(Integral)类型
整数正常分为十进制(无任何前缀)、十六进制(0x
或 0X
前缀)、二进制(0b
或 0B
前缀)。
可以将数字分隔符(_
)用于所有的整数类型。
类别 | 别名 | 位(Bit) | 范围/精度 | 默认值 | .NET API |
---|---|---|---|---|---|
有符号整型 | sbyte | 8 | -128 … 127 | 0 | System.SByte结构 |
short | 16 | -32768 … 32767 | 0 | System.Int16结构 | |
int | 32 | -2,147,483,648 … 2,147,483,647 | 0 | System.Int32结构 | |
long (后缀L) | 64 | -9,223,372,036,854,775,808 … 9,223,372,036,854,775,807 | 0 | System.Int64结构 | |
nint | 32或64 | 取决于平台 | 0 | System.IntPtr结构 | |
无符号整型 | byte | 8 | 0 … 255 | 0 | System.Byte结构 |
ushort | 16 | 0 … 65535 | 0 | System.UInt16结构 | |
uint (后缀U或u) | 32 | 0 … 4,294,967,295 | 0 | System.UInt32结构 | |
ulong (后缀U或u或L) | 64 | 0 … 18,446,744,073,709,551,615 | 0 | System.UInt64结构 | |
nuint | 32或64 | 取决于平台 | 0 | System.UIntPtr结构 |
3.十进制浮点(Decimal)类型
可以将数字分隔符(_
)用于所有的浮点类型。
别名 | 位(Bit) | 范围/精度 | 默认值 | .NET API |
---|---|---|---|---|
decimal (后缀M或m) | 128 | ±1.0 × 10-28到±7.9228162514264337593543950335 × 1028,大约 28-29 位数字 | 0.0m | System.Decimal结构 |
4.IEEE二进制浮点(Floating-point)类型
浮点类型遵循IEEE 754二进制浮点算术标准。
浮点类型还包括三个特殊的值:NaN
(非数字)、正无穷、负无穷。
可以将数字分隔符(_
)用于所有的浮点类型。
别名 | 位(Bit) | 范围/精度 | 默认值 | .NET API |
---|---|---|---|---|
float (后缀F或f) | 32 | ±1.401298 × 10−45到±3.40282347 × 1038,大约 6-9 位数字 | 0.0f | System.Single结构 |
double (不带后缀或后缀D或d) | 64 | ±4.94065645841247 × 10-324到±1.7976931348623157 × 10308,大约 15-17 位数字 | 0.0d | System.Double结构 |
5.布尔(Boolean)类型
别名 | 位(Bit) | 说明 | 默认值 | .NET API |
---|---|---|---|---|
bool | 8 | 布尔值 true 或 false | false | System.Boolean结构 |
6.Unicode 字符(Char)类型
别名 | 位(Bit) | 说明 | 范围 | 默认值 | .NET API |
---|---|---|---|---|---|
char | 16 | Unicode UTF-16 字符 | U+0000 到 U+FFFF | U+0000或\0 | System.Char结构 |
//以下都表示字符j
//字符自变量
'j'
//Unicode 转义序列
'\u006A'
//十六进制转义序列
'\x006A'
//字符代码的值转换为相应的char值
(char)106
对于 Unicode 转义序列,必须指定全部四位十六进制值。 也就是说,\u006A
是一个有效的转义序列,而 \u06A
和 \u6A
是无效的。
对于十六进制转义序列,可以省略前导零。 也就是说,\x006A
、\x06A
和 \x6A
转义序列是有效的,并且对应于同一个字符。
7.枚举(Enum)类型
别名 | 说明 | 默认值 | .NET API |
---|---|---|---|
enum | 格式为 enum E {...} 的用户自定义类型 | (E)0 | System.Enum类 |
8.元组(ValueTuple)值类型
类型 | 说明 | 默认值 | .NET API |
---|---|---|---|
ValueTuple | 格式为 (T1, T2, ...) 的用户自定义类型,T 为字段。 | —————— | System.ValueTuple结构 |
9.Nullable<T>值类型
类型 | 说明 | 默认值 | .NET API |
---|---|---|---|
Nullable<T> | T 类型 表示值必须是 T 类型的值,T? 类型 表示值可以是 T 类型的值 或 null 值。 | —————— | System.Nullable<T>结构 |
10.类(Class)类型
别名 | 说明 | 默认值 | .NET API |
---|---|---|---|
class | 格式为 class C {...} 的用户自定义类型 | —————— | —————— |
11.接口(Interface)类型
别名 | 说明 | 默认值 | .NET API |
---|---|---|---|
interface | 格式为 interface I {...} 的用户自定义类型 | —————— | —————— |
12.Unicode 字符串(String)类型
string
类型表示零个或多个 Unicode 字符的序列。
别名 | 说明 | 默认值 | .NET API |
---|---|---|---|
string | Unicode UTF-16 字符序列 | null | System.String类 |
13.数组(Array)类型
别名 | 说明 | 默认值 | .NET API |
---|---|---|---|
array | 一维、多维和交错。 例如:int[] 、int[,] 和 int[][] | null | System.Array类 |
14.元组(Tuple)引用类型
类型 | 说明 | 默认值 | .NET API |
---|---|---|---|
Tuple | 格式为 (T1, T2, ...) 的用户自定义类型,T 为属性。 | null | System.Tuple类 |
15.委托(Delegate)类型
别名 | 说明 | 默认值 | .NET API |
---|---|---|---|
delegate | 格式为 delegate int D(...) 的用户自定义的类型 | null | System.Delegate类 |
16.Nullable<T>引用类型
在C#8.0(不包含)之前,引用类型不支持T?
形式的语法,T
类型 表示值可以是 T
类型的值 或 null
值。
在C#8.0(包含)之后,可以通过两种方式控制可为 null 的上下文:在项目配置文件中设置 和 在单个 C# 源文件中添加预处理器指令。在同时配置了两种方式的情况下,预处理器指令会覆盖项目配置文件中的设置。
当在启用可为 null 的上下文的情况下,T
类型 表示值必须是 T
类型的值,T?
类型 表示值可以是 T
类型的值 或 null
值。
当在禁用可为 null 的上下文的情况下,引用类型不支持T?
形式的语法,T
类型 表示值可以是 T
类型的值 或 null
值。
注:警告的意思编译器会发出警告消息,批注的意思是在代码下面显式波浪线(“~~~~~”)批注。
在项目配置文件中设置 | 可为 null 上下文 |
<Nullable>enable</Nullable> | 开启 |
<Nullable>disable</Nullable> | 禁用 |
<Nullable>warnings</Nullable> | 警告 |
<Nullable>annotations</Nullable> | 批注 |
在单个 C# 源文件中添加预处理器指令 | 说明 |
#nullable enable | 将可为空批注和警告上下文设置为“已启用”。 |
#nullable disable | 将可为空批注和警告上下文设置为“已禁用”。 |
#nullable restore | 将可为空批注和警告上下文还原为项目配置文件。 |
#nullable enable annotations | 将可为空批注上下文设置为“已启用”。 |
#nullable disable annotations | 将可为空批注上下文设置为“已禁用”。 |
#nullable restore annotations | 将可为空批注上下文还原为项目配置文件。 |
#nullable enable warnings | 将可为空警告上下文设置为“已启用”。 |
#nullable disable warnings | 将可为空警告上下文设置为“已禁用”。 |
#nullable restore warnings | 将可为空警告上下文还原为项目配置文件。 |
17.Void返回类型
类型 | 说明 | 默认值 | .NET API |
---|---|---|---|
Void | 指定无返回值的方法的返回值类型 | —————— | System.Void结构 |
18.匿名类型
18.1创建匿名类型的对象
匿名类型提供了一种方便的方法,可以将一个或多个public
只读属性封装到单个对象中,而无需先显式定义类型。类型名称由编译器生成。每个属性的类型由编译器推断。
//只使用对象初始值设定项创建匿名类型的对象。
var 变量 = new { 属性名1 = 值1, 属性名2 = 值2, 属性名N = 值N};
//调用属性
变量.属性名
//赋值运算符左侧指定属性名称,右侧为值。
var v = new { Amount = 108, Message = "Hello" };
Console.WriteLine(v.Amount + v.Message);
//赋值运算符左侧未指定属性名称,右侧为调用其它类型的属性,则编译器会给左侧指定与其它类型的属性名称相同的名称。
//匿名类型通常用在查询表达式的 select 子句中。
var productQuery =
from prod in products
select new { prod.Color, prod.Price };
foreach (var v in productQuery)
{
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}
18.2匿名类型的类型转换
匿名类型是 class
类型,它们直接派生自 object
,并且无法强制转换为除 object
外的任何类型。
18.3匿名类型的相同类型
如果程序集中的两个或多个匿名对象初始值设定项
指定的属性具有相同的类型和名称且顺序相同,则编译器将对象视为相同类型的实例。它们共享同一编译器生成的类型信息。
18.4非破坏性变化(nondestructive mutation)
匿名类型支持 with
表达式形式的非破坏性变化,这使您能够创建一个匿名类型的新实例,其中一个或多个属性具有新值:
var apple = new { Item = "apples", Price = 1.35 };
var onSale = apple with { Price = 0.79 };
Console.WriteLine(apple);
Console.WriteLine(onSale);
19.dynamic类型
dynamic
类型(翻译为:动态类型)是一种静态类型,但类型为 dynamic
的对象会跳过静态类型检查,会在运行时类型检查,相当于将静态类型转换为动态类型。
static void Main(string[] args)
{
ExampleClass ec = new ExampleClass();
// ec调用exampleMethod1会产生编译器错误,因为
exampleMethod1只定义了一个形参
//ec.exampleMethod1(10, 4);
dynamic dynamic_ec = new ExampleClass();
// dynamic_ec调用exampleMethod1不会产生编译器错误,会产生运行时异常
dynamic_ec.exampleMethod1(10, 4);
// 无论方法是否存在,都不会引起编译器错误,会产生运行时异常
dynamic_ec.someMethod("some argument", 7, null);
dynamic_ec.nonexistentMethod();
}
class ExampleClass
{
public ExampleClass() { }
public ExampleClass(int v) { }
public void exampleMethod1(int i) { }
public void exampleMethod2(string str) { }
}
20.编译时类型和运行时类型
//编译时类型为 object,运行时类型为 string。
object anotherMessage = "This is another string of characters";
21.托管(Managed)类型和非托管(Unmanaged)类型
托管(Managed)类型:它是指受垃圾回收器管理的类型。
非托管(Unmanaged)类型:术语“非托管类型”有点误导人:它不是在非托管代码中定义的类型。它是指不受垃圾回收器管理的类型。
如果某个类型是以下类型之一,则它是非托管类型 :
- 任何只包含非托管类型的字段的用户自定义的封闭式或开放式构造结构类型。
sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
、float
、double
、decimal
、bool
、char
。- 任何枚举类型
- 任何指针类型
从 C# 7.3 开始,可使用 unmanaged
约束定义一个“类型参数为非指针、非Nullable”的非托管类型。
22.类型转换
22.1隐式转换
隐式转换始终会成功且不会导致数据丢失,因此无需使用任何特殊语法。
//从范围较小的整数值类型到范围较大的整数值类型的隐式转换
int num = 2147483647;
long bigNum = num;
//从派生类引用类型到直接或间接的基类或接口引用类型的隐式转换
Derived d = new Derived();
Base b = d;
22.2显式转换(强制转换)
显式转换,又称强制转换,可能会导致数据丢失,因此必须使用强制转换表达式。
//从精度较高或范围较大的数值值类型到精度较低或范围较小的数值值类型的显式转换
double x = 1234.7;
int a;
a = (int)x;
System.Console.WriteLine(a);
// Output: 1234
//从基类引用类型到派生类引用类型的显式转换
//隐式转换到基类是安全的
Giraffe g = new Giraffe();
Animal a = g;
//显式转换到派生类型,如果右侧的a实际不是长颈鹿类型,将在运行时抛出异常
Giraffe g2 = (Giraffe)a;
23.装箱(Boxing)和取消装箱(Unboxing)
23.1装箱
装箱是将值类型隐式或显式转换为 object
类型 或 由此值类型实现的任何接口类型。
int i = 123;
// 隐式装箱i
object o = i;
// 显式装箱i
object o = (object)i;
23.2取消装箱
取消装箱是从 object
类型到值类型或从接口类型到实现该接口的值类型的显式转换。
// 显式取消装箱
int j = (int)o;
24.协变(Covariance)和逆变(Contravariance)
例如:Cat
类型 是 Animal
类型的子类型。
协变(covariant):因为IEnumerable<T>
中的T
是协变的,所以子类型关系被保持(preserved),IEnumerable<Animal>
=
IEnumerable<Cat>
。
逆变(contravariant):因为Action<T>
中的T
是逆变的,所以子类型关系被反转(reversed),Action<Cat>
=
Action<Animal>
。
不变(invariant):因为IList<T>
中的T
是不变的,所以子类型关系被忽略(ignored),无论 IList<Cat>
还是 IList<Animal>
都不可以赋值给对方。
协变和逆变能够实现 数组类型、委托类型类型参数、泛型类型参数 的引用类型的隐式转换。
24.1协变
//数组类型
object[] array = new String[10];
//委托类型类型参数(方法返回类型)
Func<T>
//泛型类型参数(方法返回类型)
IEnumerable<Object> listObjects = new List<String>();
//使用 out 关键字将委托类型类型参数(方法返回类型)声明为协变
public delegate R DCovariant<out R>();
//使用 out 关键字将泛型类型参数(方法返回类型)声明为协变
interface ICovariant<out R>
{
R GetSomething();
}
24.2逆变
//委托类型类型参数(方法参数类型)
Action<T>
//使用 in 关键字将委托类型类型参数(方法参数类型)声明为逆变
public delegate void DContravariant<in A>(A a);
//使用 in 关键字将泛型类型参数(方法参数类型)声明为逆变
interface IContravariant<in A>
{
void SetSomething(A sampleArg);
}
原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/csharp/csharplang/7858.html