C#Lectures - 第 1 讲: 原始类型






4.92/5 (52投票s)
我正在为我们的工程师准备一系列 C# 讲座。第一讲是关于 C# 内置类型,也称为原始类型。
全部课程集
- C#Lectures - 第 1 讲:
原始类型 - C# 课程 - 第2课:在 C# 中处理文本:char, string, StringBuilder, SecureString
- C# 讲座 - 第 3 讲:在 C# 中设计类型。您需要了解的类基础知识
- C# 课程 - 第4课:面向对象编程基础:C# 示例中的抽象、封装、继承、多态
- C# 讲座 - 第 5 讲:C# 示例中的事件、委托、委托链
- C# 课程 - 第6课:C# 中的特性和自定义特性
- C# 讲座 - 第 7 讲:
反射( 通过 C# 示例) - C# 课程 - 第8课:灾难恢复。C# 示例中的异常和错误处理
- C# 讲座 - 第 9 讲:
Lambda 表达式 - C# 课程 - 第10课:LINQ 简介,LINQ to Objects 第一部分
- C# 讲座 - 第 11 讲:LINQ to 0bjects 第二部分:非延迟运算符
引言
这是我在公司为我们的工程师做的一系列 C# 讲座中的第一篇文章。它讲述的是 C# 的内置类型。
原始类型基础知识
每种编程语言都有自己的一套基本内置数据类型,我们可以利用它们和面向对象的方法来构建更复杂的数据类型,例如类和结构。C# 有自己的一套基本类型。这些类型是值类型,这意味着当我们将其作为参数传递给函数时,它们会被复制。在本文中,我想展示一些关于值类型的重要而有趣的东西。
值类型有两种
- 内置值类型
- 用户定义的值类型
C# 中的基本内置类型存储在 System
命名空间下,它们被称为原始类型。由于原始类型经常使用,C# 有一种简化的创建方式。而不是像这样编写代码
System.Double doubleDigit = new System.Double();
我们可以使用更简单的方式
double doubleDigit = 0;
实际上,我们有几种方法可以在 C# 中定义原始类型变量,由开发人员决定使用哪一种
int i =0;
int j = new int();
System.Int32 k = 0;
System.Int32 j = new System.Int32();
原始类型的局部变量在使用前必须初始化。
在两种情况下,当您构建项目时,IL 编译器都会生成相同的 IL 代码。所有 C# 原始类型在 .NET Framework 类库中都有对应的类型。例如,C# 中的 int
对应于 FCL 中的 System.Int32
。下表显示了 C# 类型及其在 MSDN 中的 FCL 代表
短名称 | .NET 类 | 类型 | 宽度 | 范围(位) |
byte | 字节型 | 无符号整数 | 8 | 0 到 255 |
sbyte | SByte | 有符号整数 | 8 | -128 到 127 |
int | Int32 | 有符号整数 | 32 | -2,147,483,648 到 2,147,483,647 |
uint | UInt32 | 无符号整数 | 32 | 0 到 4294967295 |
short | Int16 | 有符号整数 | 16 | -32,768 到 32,767 |
ushort | UInt16 | 无符号整数 | 16 | 0 到 65535 |
long | Int64 | 有符号整数 | 64 | -9223372036854775808 到 9223372036854775807 |
ulong | UInt64 | 无符号整数 | 64 | 0 到 18446744073709551615 |
float | Single | 单精度浮点类型 | 32 | -3.402823e38 到 3.402823e38 |
double | 双精度浮点型 | 双精度浮点类型 | 64 | -1.79769313486232e308 到 1.79769313486232e308 |
char | Char | 单个 Unicode 字符 | 16 | 文本中使用的 Unicode 符号 |
bool | 布尔值 | 逻辑布尔类型 | 8 | 真或假 |
object | 对象 | 所有其他类型的基类型 | ||
字符串 | 字符串 | 一串字符 | ||
decimal | 十进制 | 精确的小数或整数类型,可以表示具有 29 位有效数字的十进制数 | 128 | ±1.0 × 10e−28 到 ±7.9 × 10e28 |
对我们来说,短名称意味着在我们的任何代码文件中,我们都有这样的代码:using byte = System.Byte
。实际上并不是这样,但你可以假设是这样。一些经验丰富的开发人员,如 Jeffrey Richter,建议始终使用 FCL 类型而不是短名称。例如,在 C# 中,long
是 System.Int64
,但其他语言可能使用 Int16
和 Int32
。此外,FCL 类型中的许多方法都有与类型名称对应的名称。例如:ReadBoolean
、ReadInt32
等。
所有原始类型都可以有条件地分为以下类型
- 整数类型:
sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
- 实浮点类型:
float
、double
- 带小数精度的实数类型:
decimal
- 布尔类型:
bool
- 字符类型:
char
- 字符串类型:
string
- 对象类型:
object
在本文中,我们将把注意力集中在前五种类型,而不讨论 string
和 object
。
布尔类型
这是最简单的原始类型——它只能保存两个值:true
或 false
。如果你觉得更方便,你可以将 true
视为 1
,将 false
视为 0
。如果你将 bool
转换为 int
,你会得到 true
将是 1
,false
将是 0
。Bool
是最简单但最常用的类型,在所有应用程序中都用于控制程序流程,你可以在所有条件运算符中看到它的使用。下面是演示 bool
类型基本操作的示例代码
//declare boolean variable and assign default value for it
//you will see later that default value for bool is false
//so we could avoid assigning it to false and simply define it as bool bVar;
bool bVar = false;
bVar = true;
if (bVar)//here we use boolean value in conditional operator
{
Console.WriteLine("We are here because our boolean value is true");
bVar = false;
}
if (!bVar)
{
Console.WriteLine("We are here because our boolean value is false");
}
字符类型
Char
值是 16 位数值。编译器将数值转换为字符字面量。Unicode 字符用于表示世界上大多数语言以及不同的特定字符集。char
的常量可以写成字符字面量、十六进制转义序列、Unicode 表示形式,或者可以从输入的整数中进行转换。
当我尝试将大于最大值的值赋给 char
时,我遇到了编译错误
但如果这发生在运行时并且编译器无法控制它,我们将会在受检查的代码中遇到溢出异常,否则将重置值。我们将在本文后面回顾溢出。
以下代码在第六个循环中将把值 0
赋给 char
变量
//when we reach maximum value for char the next one will be 0
int i = char.MaxValue - 5;
for (int j = 0; j < 7; j++)
{
cVar = (char)i;
i++;
}
整数类型
整型变量包含整数,其大小取决于为变量分配的内存大小以及变量是否带符号。带符号与否定义了整型变量是否可以保存负值。最常用的整数类型是 int
和 long
。通常,如果你需要任何整数值,你会使用 int
,如果 int
的大小不满足你的需求,你会定义 long
。Int
是 32 位整数,long
是 64 位整数。任何在 int
区间内的字面量都被视为 int
,大于此值的整数会自动被视为 long
。在 int
范围内的 long
可以通过在末尾使用“L
”或“l
”字面量来显式地视为 long
long lVar = 1L;
在包含类型特征的表格以及本文的后面部分中,我们列出了每种原始类型的范围。要决定你需要哪种整数类型,你可以参考这些部分来做出决定。
实数类型
C# 中的实数类型就是数学中已知的实数。它们由不同范围和精度的浮点类型表示。理解浮点类型中的范围和精度至关重要。范围定义了类型可以保存的最小值和最大值。精度定义了变量保存的有效数字位数。
float
类型也称为单精度实数。如果在数字字面量后使用字符“f
”或“F
”,则明确指出该数字是 float
类型。您应该知道,默认情况下所有实数都被视为 double
。float
具有七位有效数字的精度。例如,数字 0.123456789
将四舍五入为 0.1234568
。
double
类型被称为双精度实数。在数字字面量后使用后缀“d
”和“D
”来表示该数字是 double
类型。double
的精度是 15 到 16 位有效数字。您应该知道 double
具有特殊值 Double.PositiveInfinity
和 Double.NegativeInfinity
。
decimal
类型被称为十进制浮点算术,其中数字以十进制系统而不是二进制系统表示。这种类型在存储和处理浮点数时不会损失精度。Decimal
类型具有 28 到 29 位有效数字的精度。数字字面量末尾的“m
”字符表示该数字是 decimal
类型。
类型特性
除 string
之外的所有原始类型都派生自 System.ValueType
类,而 System.ValueType
又派生自 System.Object
。这意味着我们可以在任何原始类型上调用 System.Object
的方法,并且所有原始类型都派生自 System.Object
,这符合 .NET 方法论。例如,调用 GetType
,您总是可以获得与短名称对应的 FCL 类型。此外,还可以使用 typeof
运算符获取变量的类型。当我们声明 ValueType
的子类时,它们会自动存储在堆栈中。基于此,它们非常快速和高效。
以下代码片段向您展示了 C# 中少数值类型的一些有趣特性
ushort ushortVar = new ushort();
Console.WriteLine("ushort type" +
" \n\t minimum value: " + ushort.MinValue +
" \n\t maximum value: " + ushort.MaxValue +
" \n\t default value for unassigned variable: " + ushortVar +
" \n\t FCL type: " + ushortVar.GetType() +
" \n\t size in bytes: " + sizeof(ushort));
long longVar = new long();
Console.WriteLine("long type" +
" \n\t minimum value: " + long.MinValue +
" \n\t maximum value: " + long.MaxValue +
" \n\t default value for unassigned variable: " + longVar +
" \n\t FCL type: " + longVar.GetType() +
" \n\t size in bytes: " + sizeof(long));
ulong ulongVar = new ulong();
Console.WriteLine("ulong type" +
" \n\t minimum value: " + ulong.MinValue +
" \n\t maximum value: " + ulong.MaxValue +
" \n\t default value for unassigned variable: " + ulongVar +
" \n\t FCL type: " + ulongVar.GetType() +
" \n\t size in bytes: " + sizeof(ulong));
float floatVar = new float();
Console.WriteLine("float type" +
" \n\t minimum value: " + float.MinValue +
" \n\t maximum value: " + float.MaxValue +
" \n\t default value for unassigned variable: " + floatVar +
" \n\t FCL type: " + floatVar.GetType() +
" \n\t size in bytes: " + sizeof(float));
这是上面程序的运行结果
如您所见,我们可以使用 System.Object
的 GetType
方法读取 C# 原始
类型的 FCL 类型。此外,使用 sizeof
运算符,我们可以读取为 原始
类型变量分配的字节大小。我们还获得了可能类型的最大值和最小值。
(程序的完整版本,请参见附件。)
类型转换
原始类型之间有两种类型转换
- 隐式类型转换 - 编译器自动将一种类型转换为另一种类型。这发生在类型转换“安全”且没有数据丢失的情况下。例如,从
Int32
转换为Int64
。 - 显式类型转换 - 当开发人员承担风险并显式设置要转换值的类型时。
示例
short short_value = 10;
int int_value = short_value; //implicit casting
byte byte_value = (byte) short_value; //explicit casting
C# 规范中“转换”部分描述了 C# 在“不安全”情况下(当值可能大于要转换的类型的最大值时)如何转换值的规则。如果您在进行显式转换时拿不准,我建议您编写一个包含可能输入值的简短测试应用程序并尝试转换它们。
溢出
当您使用原始类型,尤其是那些值范围不大的类型(例如 short
或 byte
)时,您可能会遇到一个非常敏感的话题,称为溢出。例如,如果 byte
变量的值等于 200
,并且您向它添加 60
。会发生什么?byte
的最大值是 255
,所以我们这里没有地方容纳 260
。默认情况下,C# 不会对此类情况以异常响应,而只是将 55
添加到原始值,然后当达到最大值时,我们从 0
开始。换句话说,200 + 60 = 5
。基于这个事实,您在使用溢出这种事情时应该非常小心,有时您可以将其用于自己的目的并调整您的应用程序以适应这种情况,但有时这可能是一个非常有效地隐藏的错误,很难捕获。为了避免溢出并获得系统对此的反应,您可以使用编译器选项 add.ovf
,当您遇到溢出时,它将生成 System.OverflowException
。您还可以对部分代码使用 checked 指令,此代码也将生成相同的异常。如您所见,开发人员可以选择控制溢出,但您应该熟悉可用的工具。请参阅下面的代码片段,它演示了溢出
byte v = 255;
Console.WriteLine("Insert any positive value:");
int c = Console.Read();
//here we have value of byte that reached more than maximum value, but
//the result is a new we didn't receive overflow exception since by default
//compiler doesn't react to overflow for primitive types and
//not generate System.OverflowException
//to add the check for oveflow you need to add "/checked+" to compiler command line
byteVar = (byte)(v + c);
Console.WriteLine("New byte is: " + byteVar);
//we have also another way to make operation checked
try
{
byteVar = checked((byte)(v + c));
}
catch (OverflowException e)
{
Console.WriteLine("Overflow exception catched: " + e.Message);
}
您可以使用编译器选项 /checked+,然后系统将控制每个加法、减法、乘法和类型转换操作的溢出,并在发生时引发 OverflowException
。如果您使用此选项,程序会运行缓慢,但我建议您至少在测试阶段使用此选项。
附注:我这里缺少 string
类型,因为我的下一讲将是关于 C# 中的文本处理,string
将在那里详细描述。
列表
显示原始类型示例的小型应用程序的完整列表可作为附件提供。
来源
- Jeffrey Richter - CLR via C#
- Andrew Troelsen - Pro C# 5.0 and the .NET 4.5 Framework
- MSDN
- http://www.introprogramming.info/english-intro-csharp-book/read-online/chapter-2-primitive-types-and-variables/
- http://www.dreamincode.net/forums/topic/217947-c%23-primitives-built-in-types