☰
C#数据类型详解:值类型范围、隐式/显式转换与TryParse实践
2026/10/28 11:23:18 网站建设 项目流程

简介:《C#数据类型与变量》是一份面向编程初学者、高校C#课程实验参与者与.NET入门学习者的实验型文档,围绕强类型语言中的类型分类、变量声明与取值范围展开,帮助读者厘清整型、浮点型、字符型、布尔型及字符串等常用数据类型的使用边界,并对变量定义与内存占用形成整体认识。压缩包内含1个doc文件,约1.21MB,以实验指导书形式组织,便于按步骤对照练习与课堂复现。文档以Windows窗体应用实验为载体,将byte、sbyte、short、int、long、float、double、decimal、char、bool等类型集中到ComboBox中,选择后动态显示sizeof结果及MaxValue、MinValue,直观呈现各类型占用字节与数值范围,类型选择与结果反馈的联动设计也能帮助读者建立类型、字节与范围之间的对应关系。已有353人学习,适合需要夯实C#数据类型基础、理解变量存储需求与取值限制的读者,也可作为自学对照材料,为后续编写高效、少出错的代码打底。

1. 一个抽屉的故事:数据类型到底在解决什么问题

搬进新租的房子,最让人头疼的往往不是打扫,而是整理:衣柜的格子就那么大,你把厚被子硬塞进去,抽屉合不上;把一摞袜子放进大衣柜,又觉得浪费空间。写程序也一样,内存就是那排格子,数据类型就是给每个格子定下的尺寸和用途——它规定这块地方放什么、能放多大、放不下时会发生什么。很多人学 C# 的第一课就是数据类型,却常常跳过它直接抄代码,结果被一句“值超出范围”或者“输入字符串格式不正确”卡住半天。搞懂数据类型,本质上是搞懂两件事:数据在机器里长什么样,以及它在代码里能变成什么样。把这两件事理顺了,再去敲那份实验代码,会轻松很多。

2. 为什么要给数据分门别类:强类型语言到底“强”在哪

先别急着背那十三个类型名。背下来也用不上,因为大部分人记不住ushort的上限到底是六万多还是三万二。更值得先想清楚的是:为什么一门语言非得给数据分门别类?不分类行不行?答案藏在内存的工作方式里。

2.1 内存是一排编了号的储物格

内存可以被想象成一条极长的走廊,走廊两侧全是编了号的小格子,每个格子能放 8 个二进制位,也就是 1 个字节。编号就是地址。CPU 要读数据时,它只做两件事:从某个地址开始,连续读若干个格子;然后把读到的这串 0 和 1 拿去用。

问题在于,这串 0 和 1 本身没有任何含义。同样 8 个格子,里面全是 1,写成11111111。如果你按无符号整数去解释,它是 255;如果按有符号整数去解释,它是 -1。同样的比特,两种答案,谁都没错,区别只在于“按什么规则解释”。

再举一个更极端的例子。下面这 4 个字节,按不同的类型去读,会得到完全不同的东西:

// 同一段内存,两种读法 byte[] raw = { 0x00, 0x00, 0x80, 0x3F }; float asFloat = BitConverter.ToSingle(raw, 0); // 1.0 int asInt = BitConverter.ToInt32(raw, 0); // 1065353216

这段代码做的事情很朴素:把同一块内存按 float 读一遍,再按 int 读一遍。结果是1.0和1065353216。这不是魔法,只是解释规则不同。

所以数据类型提供的是两样东西:占几个格子,和这串比特按什么规则翻译。少了任何一样,机器就没法干活——它不知道该从哪儿读到哪儿,也不知道读完该怎么算。

2.2 “强类型”不是形容词,是一组硬规矩

C# 被称为强类型语言,这个“强”字经常被误解成“严格”“难学”。它真正的意思要窄得多:每一个变量、每一个表达式,在编译期就有一个确定的类型,并且编译器会拿这个类型去检查每一次赋值、每一次运算、每一次传参。

对比一下就很清楚。在一些动态语言里,写下"1" + 1,运行时会得到字符串"11",因为语言替你做主,把数字悄悄变成了文本。而在 C# 里,下面这几行根本过不了编译:

int count = 1; bool flag = true; // 编译错误:运算符 "+" 无法应用于 int 和 bool 类型的操作数 var wrong = count + flag; // 编译错误:无法将类型 string 隐式转换为 int int number = "42";

编译器不会等到运行时才告诉你“出问题了”,它在打包之前就把这类错误拦下来。这份实验资料里那些看起来啰嗦的写法——先声明byte b;再赋值,先long.Parse再用if判断范围——背后的逻辑就是这个:类型对不上,代码根本走不到运行那一步。

强类型换来三样实际好处。错误提前暴露,不用等到用户点了按钮才发现类型错了。编辑器能真正理解你的代码,因为类型确定,所以能补全、能跳转、能重构。运行时少猜,机器不需要在每次运算前先问一句“这到底是个啥”。代价是写的时候要多想一步,多打几个字。

这里要顺手澄清一个常见误解:var、object、dynamic的出现,并没有让 C# 变弱类型。var只是让你省掉类型名,编译器照样会推断出一个确定的类型,推断完就固定了;dynamic是把检查从编译期推迟到运行期,类型依然存在,只是编译器不替你查了。这两者的区别,后面第 5 章还会细说。

2.3 十三个内置值类型,各自管一摊事

那份实验资料的下拉框里放了十三个类型名,这个数量不是随便凑的,它正好是 C# 里内置的值类型全家福。按家族分,是三拨人:整数、浮点数、以及两个单独的(字符和布尔)。

类型字节取值范围常见用途
byte10 ~ 255二进制数据、图片像素
sbyte1-128 ~ 127少见,多用于互操作
short2-32768 ~ 32767老式协议字段
ushort20 ~ 65535端口号、码点
int4-2147483648 ~ 2147483647计数器、索引,最常用
uint40 ~ 4294967295位标志、哈希值
long8约 ±9.2×10¹⁸时间戳、大 ID
ulong80 ~ 约 1.8×10¹⁹少见
float4约 ±3.4×10³⁸,7 位有效数字图形、科学计算
double8约 ±1.7×10³⁰⁸,15~17 位有效数字默认浮点类型
decimal16约 ±7.9×10²⁸,28~29 位有效数字金额、财务计算
char2U+0000 ~ U+FFFF单个字符
bool1true/false条件判断

这张表值得多看两眼,因为它藏着几条规律。

整数家族按字节数翻倍排列:1、2、4、8。每翻一倍,能表示的数的个数就翻 2 的 8 次方倍,也就是 256 倍。byte有 256 个值,short有 65536 个值,int有约 42 亿个值。有符号和无符号的区别只在于:同样多的比特,是把一半分给负数,还是全给非负数。byte全给非负,所以是 0 到 255;sbyte分一半给负数,所以是 -128 到 127。

浮点家族只有三个成员,但它们的差别不在“大小”,而在“怎么存”。float和double用的是二进制科学计数法,尾数是二进制小数,所以像 0.1 这种十进制里很规整的数,在二进制里是无限循环的,只能存一个近似值。decimal换了个思路,它内部用十进制来存,所以 0.1 就是 0.1,做加减乘除不会冒出0.30000000000000004这种尾巴。代价是慢一些、占地方一些,而且范围小得多。

char是 2 个字节,因为它存的是 UTF-16 的码元,不是 ASCII 字符。这一点让它和byte之间不能随便互转,后面讲转换时会再碰到。

bool名义上占 1 个字节,但它真正的价值不在大小,而在于它把“真/假”这件事从数字里摘了出来。C 语言里 0 是假、非 0 是真,C# 不允许这么混,if (1)是编译错误。这么设计是为了让代码读起来没有歧义。

还要补一句:string不在上面这张表里。它是引用类型,不是值类型,虽然日常用得比谁都多。实验里的下拉框没列它,是对的。

3. 把类型摊开来看:字节数、取值范围和变量的关系

知道了有哪些类型,接下来的问题是:这些数字从哪来?为什么int的上限偏偏是 2147483647,不多不少?为什么decimal的最小值写出来那么长一串?把这些搞明白,就不用死记硬背了。

3.1 sizeof、MaxValue、MinValue 这三个东西是怎么算出来的

那份实验资料的核心玩法,就是选一个类型,然后把它的字节数、最大值、最小值显示出来。用到的三个东西分别是sizeof运算符,以及每种类型自带的MaxValue和MinValue静态字段。

// 三行代码就能把一种类型看个底朝天 Console.WriteLine(sizeof(byte)); // 1 Console.WriteLine(byte.MaxValue); // 255 Console.WriteLine(byte.MinValue); // 0 Console.WriteLine(sizeof(decimal)); // 16 Console.WriteLine(int.MaxValue); // 2147483647 Console.WriteLine(int.MinValue); // -2147483648

sizeof返回的是这种类型占多少个字节,它在编译期就能算出来,所以括号里可以直接写类型名。对于自定义的结构体,sizeof只能在 unsafe 上下文里用,但对这十三个内置类型没有这个限制。

MaxValue和MinValue是两个常量字段,值在编译时就定死了。它们的数值不是拍脑袋定的,而是从位数推出来的:n 个比特能表示 2ⁿ 个不同的值。无符号类型把这 2ⁿ 个值全分给非负数,于是范围是 0 到 2ⁿ-1。byte是 8 位,2⁸=256,所以是 0 到 255。

有符号类型用的是一种叫补码的编码方式,最高位当符号位,剩下 n-1 位表示数值。这种编码的好处是减法可以当加法做,硬件简单。代价是正负不对称:8 位有符号整数能表示 -128 到 127,负数比正数多一个。所以sbyte是 -128 到 127,short是 -32768 到 32767,int是 -2147483648 到 2147483647。

记住这个不对称性,很多“为什么最大值比最小值的绝对值小 1”的疑惑就自动消失了。

浮点的范围推导要复杂一些,涉及 IEEE 754 标准的指数位和尾数位分配,日常不需要手算。但有件事值得知道:float和double的MaxValue是能表示的最大有限值,MinValue是最小的有限值,也就是负得最多的那个数,而不是最小正数。这一点后面第 5 章会专门拆开讲,因为它是初学者最容易翻车的地方之一。

3.2 变量不是标签,是格子本身

很多人对变量的第一印象是“给一个值起个名字”,这个理解不算错,但会带来一连串误解。更准确的说法是:变量是一块有名字、有类型、有地址的存储位置。

写int age = 25;的时候,运行时发生的事情是:在栈上划出 4 个字节,把这 4 个字节命名为age,然后把 25 的二进制补码写进去。这块地方从划出来那一刻起,形状就固定了,它能装 4 个字节,按有符号整数解释。

于是就有了那条铁律:**变量的类型一旦声明,终身不能改。**下面这两行会有截然不同的待遇:

int age = 25; age = 30; // 可以,改的是格子里装的值 age = "三十"; // 编译错误,格子的形状改不了

改值可以,改类型不行。想要装别的东西,只能再声明一个新变量。

这个“格子”的比喻还能解释另一件事:值类型的变量,格子里装的就是值本身;引用类型的变量,格子里装的是一个指向别处的地址。string属于后者。所以说string s = "abc"时,s这个格子里装的不是字母 a、b、c,而是一个“它们在别处”的线索。这个区别在赋值和比较时会引发一连串行为差异,不过在数据类型这一层,只需要记住它和那十三个值类型不是一路的。

另外,var不会破坏这条铁律。var n = 10;编译器推出来是int,那它就是int,后面再写n = "十";照样报错。var省的是打字的手,不是类型的约束。

3.3 声明之后发生的事:默认值与赋值时机

一个变量从声明到能用,中间隔着一件事:它到底有没有值。

这件事在 C# 里分两种情况,区别很大。类的字段有默认值,声明完不用赋值就能读:整数是 0,浮点是 0,bool是false,char是'\0',引用类型是null。方法里的局部变量没有默认值,不赋值就直接用,编译器会拦下来。

class Sample { private int fieldCount; // 字段,默认就是 0 public void Run() { Console.WriteLine(fieldCount); // 输出 0,没问题 int localCount; Console.WriteLine(localCount); // 编译错误 CS0165:使用了未赋值的局部变量 } }

这个设计不是故意刁难人。字段的默认值有明确的初始化时机,而局部变量的生命周期太短,编译器没法保证它在每个分支上都一定被赋过值,那就不如强制你先赋值。数组元素是另一回事,new int[5]出来的五个元素都是 0,因为数组在创建时会整块清零。

回到那份实验。窗体上的三个文本框在程序刚启动时是空的,这不是因为它们“默认是空字符串”,而是因为控件加载时还没人往里写过东西。Form1_Load里那段cmbDataType.Items.AddRange(strType)干的事,就是在窗体显示之前把数据填进去。加载顺序搞错了,比如在构造函数里就急着读控件属性,就可能读到还没准备好的状态,这类问题在老版本的开发环境里尤其常见。

4. 类型之间的换乘:隐式转换、显式转换和 Convert

程序里真正麻烦的从来不是“一个类型”,而是“两个类型碰头”。用户从文本框敲进来的是一串字符,要存进数据库得是数字,算完又要变回字符显示出来。每一次这样的换乘,都有翻车的可能。

4.1 隐式转换:系统替你签的那张字

隐式转换的意思是:不用你写任何转换代码,编译器自己就把值从一种类型搬到另一种类型,而且不报错、不警告。它之所以敢这么干,是因为有一个基本原则——从小范围搬到大范围,不会丢东西。

byte的范围是 0 到 255,int的范围远大于它,任何一个byte值放进去都装得下,所以byte到int是隐式的。反过来就不行,因为int里有一大堆值byte根本装不下。

那份实验资料用一组数组把这个矩阵写了下来,那些impbyte、impshort、impint之类的名字,其实就是“从某个类型出发,能隐式去哪些类型”的清单。整理成表是这样:

源类型可隐式转换到
byteshortushortintuintlongulongfloatdoubledecimal
sbyteshortintlongfloatdoubledecimal
shortintlongfloatdoubledecimal
ushortintuintlongulongfloatdoubledecimal
intlongfloatdoubledecimal
uintlongulongfloatdoubledecimal
longfloatdoubledecimal
ulongfloatdoubledecimal
floatdouble
charushortintuintlongulongfloatdoubledecimal

这张表乍看零碎,但每条都能讲出理由。

sbyte能去short、int、long,却不能去ushort、uint、ulong。原因很简单:sbyte有负数,无符号类型装不下负数,编译器不敢替你做这个主。

char能去ushort和int,却去不了short。因为char的范围是 0 到 65535,而short的上限只有 32767,中间那一段塞不进去。这也是为什么字符和短整型看起来都是 2 个字节,却不能随便互转。

float只能去double,去不了decimal。这里不是范围的锅,decimal的范围比float小得多,问题出在值的种类上:float能表示“正无穷”“负无穷”“非数字”这几种特殊值,而decimal里没有这些东西。编译器没法保证转换后的值有意义,所以干脆不让隐式走。

有一处细节特别值得抠:long能隐式转float,long也能隐式转double,但long有 64 位,float的尾数只有 24 位,double的尾数只有 53 位,大数转过去一定会掉精度。编译器为什么放行?因为隐式转换保证的是“不溢出”,不是“不丢精度”。这两件事经常被混为一谈。举个具体的例子:long的 9007199254740993 转成double会变成 9007199254740992,值变了,但不报错。

4.2 显式转换:为什么要你自己签

凡是可能出问题的转换,C# 都要求你亲手写一个括号,把目标类型写在前面。这个动作相当于签字画押:我知道可能出事,我认。

int big = 300; // 显式转换,编译器不拦,但结果不是你以为的那样 byte b = (byte)big; Console.WriteLine(b); // 输出 44 // 加上 checked,溢出时抛异常 try { byte c = checked((byte)big); } catch (OverflowException) { Console.WriteLine("装不下"); }

300 变成 44 的过程是这样的:300 的二进制是1 0010 1100,占了 9 位;byte只有 8 位,于是高位被截掉,剩下0010 1100,也就是 44。默认情况下 C# 不检查这种截断,因为检查有性能开销,而且位运算场景下截断本身是正常需求。

想要检查,就用checked包起来,溢出时抛OverflowException。这个开关可以在项目级别统一设置,也可以只包住某一段代码。

显式转换还有一处坑,比溢出更隐蔽:浮点转整数是截断,不是四舍五入。

double d = 3.9; int i = (int)d; // 结果是 3,不是 4 int j = Convert.ToInt32(d); // 结果是 4

(int)直接把小数部分砍掉,Convert.ToInt32会做舍入,而且用的是“银行家舍入”——遇到 .5 时向偶数靠拢,2.5 变 2,3.5 变 4。这套规则在财务计算里更公平,但在业务代码里经常让人意外。做统计报表时如果发现总数差了一点,先回头看看是不是这里。

4.3 Convert、Parse、TryParse:字符串进来的三条路

前面讲的都是数字之间搬家。现实里更常见的是字符串和数字之间的转换,因为用户输入、配置文件、网络报文,进来的全是文本。

三条路,各有各的脾气。

Parse最直接,字符串格式不对就抛FormatException,数值超范围就抛OverflowException。它适合那种“我确信输入是合法的”场景,比如解析程序自己刚写出去的日志。

Convert.ToInt32比Parse多一层保护。传null进去它返回 0,不抛异常;传浮点数进去它会做舍入。代价是它对非法格式照样抛FormatException,而且返回 0 这个行为有时候会掩盖真正的错误。

TryParse是最稳的一条。它不抛异常,转换成功返回true并把结果写进out参数,失败返回false。用户输入这种完全不可控的来源,就该用它。

string input = "999999999999"; // 方式一:Parse,超出 int 范围会抛异常 try { int a = int.Parse(input); } catch (OverflowException) { Console.WriteLine("数字太大,int 装不下"); } // 方式二:TryParse,不抛异常,用返回值判断 if (int.TryParse(input, out int b)) { Console.WriteLine($"转换成功:{b}"); } else { Console.WriteLine("转换失败:格式不对或范围超了"); }

回头看那份实验资料里的一段代码,思路其实很讲究:

byte b; long i = long.Parse(tbInput.Text); // 先用更大的类型接住 if (i > byte.MaxValue || i < byte.MinValue) // 手动检查范围 { MessageBox.Show("the input number is out of the range of byte"); return; } b = byte.Parse(tbInput.Text); // 确认装得下,再转成 byte

它没有直接byte.Parse,而是先用long把用户输入接住。因为long的盘子大,绝大多数输入都装得下,装不下的会先被long.Parse拦下。然后再拿这个long去和byte的上下限比,确认在范围内,才放心转成byte。这个“先用大类型接,再判断范围,最后缩小”的套路,在处理用户输入时非常通用,值得抄进自己的习惯里。

还有一处细节容易看漏:实验里显示char的最大值时,用的是Convert.ToInt32(char.MaxValue),而不是直接char.MaxValue.ToString()。这不是多此一举。char.MaxValue是'\uffff',ToString()出来是一个看不见的字符,文本框里显示成空白;只有转成整数 65535,才能看出它到底是什么。同理,bool的最大值用bool.TrueString拿到的是"True"这个单词,不是数字 1。这些细节都在提示同一件事:类型的“最大值”是什么意思,取决于这个类型是什么。

5. 最容易踩坑的几组概念:初学者常见误区

类型的基本规则就那么几条,但真正写代码时出错的地方,往往不是规则本身,而是几个长得很像的概念被混在了一起。下面这五组,是那份实验资料里最容易让人卡住的地方。

5.1 byte 的最小值不是 -128:无符号和有符号的第一道分水岭

易混点:很多人一看到“一个字节的整数”,脑子里蹦出来的范围就是 -128 到 127。让他们说byte.MinValue,答案经常是 -128。

为什么混:因为教学顺序往往是先讲有符号再讲无符号,而“一个字节”这个说法对两者都成立。加上sbyte这个名字出现频率低,byte又更常用,两个概念就在记忆里糊成了一团。另一个原因是补码的知识点里,-128 这个数字太有存在感,很容易被当成“一个字节的代表值”。

怎么区分:看名字里的那个u或者s。sbyte的s是 signed,有符号,范围 -128 到 127;byte没有前缀,是 unsigned,范围 0 到 255。两者的字节数都是 1,sizeof都返回 1,唯一区别就是那 8 个比特怎么解释。写代码时如果拿不准,别背,直接跑一遍Console.WriteLine(byte.MinValue),输出 0 就是 0。

顺带一提,这个区分在实验的下拉框里体现得很直接:两个选项并排出现,选byte时最小值显示 0,选sbyte时显示 -128。跑一次比背十遍管用。

5.2 char 的“最大值”不是数字 65535:类型不同,最值的含义也不同

易混点:把char.MaxValue直接ToString()塞进文本框,屏幕上什么都没有,于是以为代码写错了。

为什么混:因为char在内存里确实存的是一个 0 到 65535 的数值,很容易被当成“小整数”来对待。既然int.MaxValue打印出来是数字,char.MaxValue凭什么不是。

怎么区分:要记住char的语义是字符,不是数字。它的MaxValue是'\uffff',一个不可见的 Unicode 字符;MinValue是'\0',空字符。想看它的码点,得显式转成整数,(int)char.MaxValue或者Convert.ToInt32(char.MaxValue),两个都返回 65535。同理,bool的最值也不是数字,bool.TrueString是字符串"True",bool.FalseString是"False"。

这条误区背后其实是一个更大的话题:类型的“值”和“显示出来的样子”是两件事。ToString()是一种格式化,不是取原始值。

5.3 float.MinValue 不是最小正数:浮点数的最值定义反直觉

易混点:看到float.MinValue,下意识理解成“最小的那个正数”,就像在说“最小的一份”。

为什么混:日常语言里“最小值”常常有“下限”和“最小正数”两种含义,中文尤其容易模糊。整数类型没有这个问题,因为int.MinValue就是负得最多的那个数,不会有人理解成 1。

怎么区分:float.MinValue和double.MinValue都是负得最多的有限值,float.MinValue约等于 -3.4×10³⁸。真正的最小正数有另一个名字,叫float.Epsilon,约等于 1.4×10⁻⁴⁵,它是能表示的最接近 0 的正数。decimal也一样,decimal.MinValue是负的最大绝对值,不是最小正数。

这个设计在数学上说得通:负数和正数是对称的,“最小”自然指最靠左的那一端。但在业务代码里,如果你的意图是“这个数不能小于 0”,那判断条件应该写value < 0,而不是拿MinValue去比。

5.4 decimal.MinusOne 不是最小值:照抄示例代码的代价

易混点:那份实验资料里,显示decimal最小值的那一分支写的是decimal.MinusOne.ToString()。跑起来一看,文本框里显示 -1。看着挺正常,其实不对。

为什么混:MinValue和MinusOne一字之差,属性列表里还挨得近。加上decimal的最小值有 29 位数字,太长,示例代码的作者可能图省事,随手写了个MinusOne让界面看起来有内容。抄的人不会去核对,因为界面上确实显示了东西。

怎么区分:decimal.MinusOne就是一个常量 -1,跟“最小”没有任何关系。decimal.MinValue是 -79228162514264337593543950335,29 位。写代码时凡是看到Min、Max开头的属性,养成习惯先Console.WriteLine一遍,看看输出对不对,别信注释,也别信示例。

这件事的意义不止于一个属性名。示例代码是给人看的,不是给编译器看的,能跑通不等于写对了。抄的时候多问一句“这行为什么这么写”,能省掉后面很多解释不清的 bug。

5.5 var 不是“没有类型”,dynamic 也不是

易混点:var x = 10;和dynamic y = 10;看起来都是“不写类型”,被当成同一类东西。

为什么混:两者都省掉了类型名,IDE 里看着差不多。而且都在“现代 C# 写法”的文章里高频出现,容易被归成一类“新语法糖”。

怎么区分:var是编译期类型推断。var x = 10;里,编译器在编译时就知道x是int,把鼠标悬停在变量上能直接看到类型,后面写x = "十";会编译报错。它要求声明时必须给初始值,因为编译器要拿初始值来推断。

dynamic是把类型检查推迟到运行时。dynamic y = 10;声明时不知道类型,运行到那一行才知道是int,写y = "十";是合法的,写y.NonExistentMethod()也能过编译,直到运行时才抛异常。它绕过了编译器的保护,换来的是灵活性,代价是性能开销和错误发现得晚。

日常写业务代码,var可以放心用,dynamic要谨慎,除非确实需要和动态语言或某些反射场景打交道。

6. 什么时候该为类型较真,什么时候可以放过它

把上面这些讲完,容易走向另一个极端:每个变量都去查表,每个转换都包三层判断,代码写得像防贼。这也没必要。真正需要判断的是场景——有些地方类型错了只是显示难看,有些地方类型错了就是真金白银的损失。

场景建议做法理由
金额、税率、单价一律decimal二进制浮点存不下 0.1,累加会漂
接口返回的 ID、时间戳long起步int撑不住 21 亿以上的值
用户输入的数字TryParse或“大类型接住再判范围”输入不可控,不能靠异常兜底
循环计数器、数组下标int够用,long反而拖慢
方法内部的临时变量var右边已经写明类型,左边不必重复
二进制数据、文件字节byte[]这是它本来的语义
开关状态bool别用 0/1 代替,读代码的人会感谢你

判断标准其实只有一条:**这个值会不会跨出当前方法。**跨出去的,进数据库、进接口、进界面显示,就得为类型较真,因为类型决定了解释方式,解释方式错了,值就变了。只在方法内部转一圈的临时变量,用var让编译器替你操心就行。

举一个具体的例子。假设要做一个订单金额汇总,用double写出来是这样:

double total = 0; foreach (var price in prices) { total += price; // 每个 price 都是 double } Console.WriteLine(total); // 可能输出 299.99999999999994

单个价格看起来没问题,加得多了,误差就攒出来了。换成decimal,同样的逻辑输出 300.00,一分不差。这个差别在测试环境里经常看不出来,因为样本少;上了生产,对账的时候才发现差了几毛钱,回头查半天。

还有一个我自己的习惯,是从一次事故之后养成的。当时写一个导入功能,用户上传的表格里有一列数量,我用int.Parse直接解。测试数据都是规规矩矩的数字,一切正常。上线第二天,有人在那列里填了一个空格,整批导入全部失败,异常信息还只说了“格式不正确”,没说是哪一行。排查花了两个小时。从那以后,凡是外部输入进来的数字,我一律用TryParse,并且在失败时把原始内容和行号一起记下来。麻烦是麻烦了一点,但那两个小时再也没出现过。

反过来,也别把这份谨慎带进所有地方。写个循环for (int i = 0; i < list.Count; i++),没必要去想i会不会超过 21 亿;写个布尔标志,没必要用int再加注释说 0 表示关。类型选得对,代码自己会说话;类型选得拧巴,注释写三行也救不回来。

那份实验资料的价值,不在于它教了十三个类型名,而在于它给了你一个可以随时翻开对照的表:选一个类型,立刻看到它占多大、能装多少。这套对照关系一旦在心里立住,后面写decimal还是double、用long还是int,就不用再犹豫了。需要把这份实验做一遍、把每个分支都跑通的人,可以顺着资料里的步骤从头敲一次,尤其是那个下拉框和转换矩阵,亲手跑过和看别人跑过,手感完全不一样。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询