简介:这份C#数据类型与变量实验文档面向C#初学者和程序设计基础课程学习者,围绕强类型语言中的数据类型分类、变量声明与使用等核心知识展开,帮助读者建立对基本数据类型与引用数据类型的系统认识,并理解变量作为数据容器的定义方式。压缩包仅含1个doc文件,大小约1.21MB,内容以实验任务书形式组织,适合课堂实验、课后自学或实验报告参考。文档结合Windows窗体应用示例,展示整型、浮点型、字符型、布尔型及字符串等常用类型,通过下拉框选择与文本框联动呈现各类型所占字节数、最大值和最小值,并附有窗体加载与选项切换事件的关键代码片段,便于读者直观比较不同数据类型的存储需求与取值范围。目前已有353人学习,适合需要巩固数据类型基础、完成基础实验或准备后续C#编程练习的读者查阅。
1. 先搞懂一件事:C# 的数据类型和变量到底在管什么
第一次在 Visual Studio 里敲下int x = 3.14;,很多人会盯着那条红波浪线发愣:明明数字没错,标点也没错,为什么就是不给过?其实编译器在跟你说一句很朴素的话——这个叫 x 的变量被你贴上了 int 的标签,它只收整数,你塞进去一个带小数点的,它装不下。C# 数据类型与变量,说白了就是给每一个装数据的「盒子」贴标签:盒子多大、能装什么、最多装多少、满了会怎样,标签上全写着。
强类型语言的好处是不让你糊里糊涂地错。它不会像某些脚本语言那样偷偷帮你转换,然后在某个深夜给你一个莫名其妙的结果。代价就是新手前两周会觉得处处碰壁:这里要转、那里要转、这里又溢出了。别慌,这些碰壁全是可预期的,一共就那么几种。
这一篇不往内存模型里钻,也不讲什么装箱拆箱的深层原理。只做一件事:带你把十几个标签认全,然后从零跑通两个能看见效果的小程序——一个是查类型有多大的查询器,一个是把一种类型转成另一种的转换器。完全没写过 C#、连控件都还没拖过的新手,跟着做就行。
2. 为什么必须先学它:C# 是强类型语言,标签贴错寸步难行
2.1 强类型到底强在哪:一个变量只认一种东西
先把「变量」这个词拆开看。变量不是数字本身,它是数字的存放位置,是一张贴了名字的便签。你写int age = 25;,编译器做的事情是:找一块 4 字节的空间,贴上名字 age,贴上类型 int,然后把 25 装进去。从此以后,age 这个名字就只认 int,你别想让它改行去装一段文字。
这就是强类型。很多人第一次听到会觉得麻烦,我一开始也这么想。但换个角度看:它把「类型不匹配」这种错误从运行期提前到了编译期。你写错的那一刻就报红,而不是等到用户点了按钮才崩。这种提前暴露,恰恰是新手最需要的东西——你还没有能力去调试一个运行到一半才炸的程序,那就让它在写的时候就炸。
再补一层:类型决定了三件事。第一是占多大内存,byte 占 1 字节,decimal 占 16 字节,差着十六倍。第二是取值范围,byte 最大 255,再大就溢出。第三是能做什么运算,数字能加减,字符串能拼接,bool 只能取反。你把这三件事想明白,后面看任何一门语言都不会太陌生。
入门阶段建议先记住一句话:不确定用什么类型时,整数用 int,小数用 double,文字用 string,真假用 bool。这四个能覆盖你前两周 90% 的场景,剩下的慢慢补。
2.2 十三个基本类型怎么分组记:整数、小数、字符、布尔
原文那份实验里列了 13 个类型名,一次性背下来肯定记不住。正确的记法是分组。
整数组一共 8 个,靠两个维度区分:有没有符号(带不带负数)、占几个字节。byte 和 sbyte 是一对,1 字节,一个只管 0~255,一个管 -128~127。short 和 ushort 是 2 字节的一对,int 和 uint 是 4 字节的一对,long 和 ulong 是 8 字节的一对。规律很明显:带 u 前缀的就是 unsigned(无符号),只收正数和零,所以正数范围翻倍。
小数组只有 3 个,区别在精度和用途。float 4 字节,精度低,写的时候要在数字后面加个 f,比如3.14f。double 8 字节,是 C# 里小数的默认类型,你直接写3.14编译器就当 double 处理。decimal 16 字节,精度最高,专门给钱的场景用——算账的时候用 double 会出现 0.1+0.2 不等于 0.3 这种玄学问题,decimal 就是为了避免它。
字符组就一个 char,2 字节,装单个字符,用单引号包起来,比如'A'。注意是单个,'AB'是非法的。中文也算一个字符,因为 C# 的 char 是 UTF-16 编码,一个 char 占两个字节。
布尔组也只有 bool,1 字节,值只有 true 和 false 两个,没有 0 和 1 这种写法。
另外还有一个 string,严格说它是引用类型不是基本类型,但你每天都在用,先把它当「装一串字符的盒子」理解就够了。它和 char 的关系是:char 是一节车厢,string 是整列火车。
2.3 变量、声明、赋值:三个词先分清
新手最容易混的是这三个动作。看一行代码:
int score = 90;这里int score;这一半叫声明——告诉编译器「我要一个叫 score 的 int 盒子」。score = 90;这一半叫赋值——把 90 放进去。两件事写在一起,就是带初始化的声明。你也可以拆开写,先声明后赋值,效果一样。
有一条硬规矩:局部变量必须先赋值才能用。你写int score;然后直接Console.WriteLine(score);,编译器会拦你,说使用了未赋值的局部变量。这不是它刁难你,是它真的不知道那块内存里现在是什么。
命名上也有几条约定,跟着走不吃亏:变量名用小驼峰,首字母小写,比如userName;名字要有意义,别用 a、b、c;不能用数字开头;不能和 C# 的关键字(int、class、if 这些)重名。这些不是强制语法,但你以后看别人的代码、别人看你的代码,全靠这套约定省力气。
还有个小细节值得早点知道:=是赋值,==才是判断相等。这个坑我见过太多人踩,而且踩了还不容易发现,因为程序不报错,只是逻辑永远不对。第 5 章会专门讲。
3. 第一个能跑的例子:做一个「数据类型查询器」窗体
3.1 建工程、摆控件:界面十分钟搭好
打开 Visual Studio,新建项目,模板选「Windows 窗体应用」(老版本叫 Windows 窗体应用程序),项目名随便起一个,比如 TypeExplorer。建好之后你会看到一个空白的 Form1 窗口,这就是你的画布。
接下来从左边工具箱往窗体上拖控件,一共 8 个:4 个 Label、1 个 ComboBox、3 个 TextBox。摆成下面这个意思就行,不用对齐得很精确:
- 第 1 个 Label 写「请选择数据类型」,放在最上面
- ComboBox 放在它下面,这是待会儿选类型的下拉框
- 第 2 个 Label 写「占用字节数」,右边配一个 TextBox
- 第 3 个 Label 写「最大值」,右边配一个 TextBox
- 第 4 个 Label 写「最小值」,右边配一个 TextBox
摆完之后最关键的一步是改名字。选中控件,在右下角属性窗口里找到 (Name) 这一项,按下表改掉。这一步千万别偷懒,用默认的 comboBox1、textBox2 写代码,三天后你自己都认不出哪个是哪个。
| 控件 | 改成的名字 | 作用 |
|---|---|---|
| ComboBox | cmbDataType | 选择数据类型 |
| 第 1 个 TextBox | tbSize | 显示占用字节数 |
| 第 2 个 TextBox | tbMax | 显示该类型最大值 |
| 第 3 个 TextBox | tbMin | 显示该类型最小值 |
改完名字,界面这部分就完成了。你会发现整个过程没写一行代码,但已经完成了一半工作量——窗体程序就是这样,界面归界面,逻辑归逻辑。
3.2 Form_Load 里把 13 个类型名塞进下拉框
现在双击窗体的空白处,Visual Studio 会自动跳到代码页,并且帮你写好一个Form1_Load方法。这个方法叫「事件处理程序」,意思是窗体加载完成的那一刻会自动执行里面的代码。我们要在这里把 13 个类型名填进下拉框。
private void Form1_Load(object sender, EventArgs e) { // 先清空,防止重复加载时出现两遍 cmbDataType.Items.Clear(); // 把 13 个基本类型名放进一个字符串数组 string[] strType = { "byte", "sbyte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "decimal", "char", "bool" }; // 一次性把数组里的内容全部塞进下拉框 cmbDataType.Items.AddRange(strType); // 默认选中第一项,避免刚打开时三个框都是空的 cmbDataType.SelectedIndex = 0; }逐句说一下。cmbDataType.Items是下拉框里所有选项的集合,Clear()就是清空,加这一句是为了防止某些情况下窗体被重新加载、选项变成两份。string[]声明了一个字符串数组,花括号里那 13 个名字必须和后面 switch 里的写法一模一样,大小写都不能差,这一点先记着,第 5 章会讲为什么。
AddRange是批量添加,参数是数组,一次全塞进去;如果只加一个,用Add。最后SelectedIndex = 0是把选中项设成第 0 项,也就是 byte。为什么要加这一句?因为下拉框默认是不选中的,如果不设,程序刚跑起来三个文本框会空着,看起来像坏了。
提示:
Form1_Load这个方法不用你手动调用,窗体显示之前系统会自动触发。你只要保证方法名和事件对得上就行。
3.3 选中就显示:SelectedIndexChanged 里的 switch
下拉框的选项有了,接下来要让它「选中谁就显示谁的信息」。回到设计界面,选中 cmbDataType,在属性窗口点那个闪电图标(事件列表),找到SelectedIndexChanged,双击它右边空白处,Visual Studio 会再帮你生成一个空方法。
这个方法的意思是:只要下拉框选中的项变了,就执行一次。我们要在里面根据选中的名字,去查这个类型的字节数、最大值、最小值。
private void cmbDataType_SelectedIndexChanged(object sender, EventArgs e) { // 防御一下:没选中任何项就直接返回,避免空引用 if (cmbDataType.SelectedItem == null) return; string strSel = cmbDataType.SelectedItem.ToString(); switch (strSel) { case "byte": tbSize.Text = sizeof(byte).ToString(); // 1 tbMax.Text = byte.MaxValue.ToString(); // 255 tbMin.Text = byte.MinValue.ToString(); // 0 break; case "sbyte": tbSize.Text = sizeof(sbyte).ToString(); // 1 tbMax.Text = sbyte.MaxValue.ToString(); // 127 tbMin.Text = sbyte.MinValue.ToString(); // -128 break; case "short": tbSize.Text = sizeof(short).ToString(); // 2 tbMax.Text = short.MaxValue.ToString(); // 32767 tbMin.Text = short.MinValue.ToString(); // -32768 break; case "ushort": tbSize.Text = sizeof(ushort).ToString(); // 2 tbMax.Text = ushort.MaxValue.ToString(); // 65535 tbMin.Text = ushort.MinValue.ToString(); // 0 break; case "int": tbSize.Text = sizeof(int).ToString(); // 4 tbMax.Text = int.MaxValue.ToString(); // 2147483647 tbMin.Text = int.MinValue.ToString(); // -2147483648 break; case "uint": tbSize.Text = sizeof(uint).ToString(); // 4 tbMax.Text = uint.MaxValue.ToString(); // 4294967295 tbMin.Text = uint.MinValue.ToString(); // 0 break; case "long": tbSize.Text = sizeof(long).ToString(); // 8 tbMax.Text = long.MaxValue.ToString(); tbMin.Text = long.MinValue.ToString(); break; case "ulong": tbSize.Text = sizeof(ulong).ToString(); // 8 tbMax.Text = ulong.MaxValue.ToString(); tbMin.Text = ulong.MinValue.ToString(); // 0 break; case "float": tbSize.Text = sizeof(float).ToString(); // 4 tbMax.Text = float.MaxValue.ToString(); tbMin.Text = float.MinValue.ToString(); break; case "double": tbSize.Text = sizeof(double).ToString(); // 8 tbMax.Text = double.MaxValue.ToString(); tbMin.Text = double.MinValue.ToString(); break; case "decimal": tbSize.Text = sizeof(decimal).ToString(); // 16 tbMax.Text = decimal.MaxValue.ToString(); tbMin.Text = decimal.MinValue.ToString(); break; case "char": tbSize.Text = sizeof(char).ToString(); // 2 // char 的 MaxValue 本身是字符,转成数字才看得懂 tbMax.Text = ((int)char.MaxValue).ToString(); // 65535 tbMin.Text = ((int)char.MinValue).ToString(); // 0 break; case "bool": tbSize.Text = sizeof(bool).ToString(); // 1 tbMax.Text = bool.TrueString; // True tbMin.Text = bool.FalseString; // False break; default: MessageBox.Show("没有选中任何类型!"); break; } }这段代码看着长,其实就是同一个动作重复了 13 次,看懂第一个 case 就全懂了。sizeof(byte)返回这个类型占几个字节,结果是数字,数字要.ToString()转成字符串才能塞进文本框的Text属性——因为 Text 只收字符串,这是新手第一个高频卡点。
byte.MaxValue和byte.MinValue是每种类型自带的两个属性,直接给你最大最小值,不用自己背。char 那两行稍微特殊:char.MaxValue本身是字符'\uffff',直接显示出来是乱码,所以用(int)强制转成整数 65535 再看。bool 更特殊,它没有数字上的最大最小,只有 True 和 False 两个字符串常量,所以用bool.TrueString。
注意:decimal 那三个值不要写成
decimal.MinusOne之类的形式,标准写法就是decimal.MinValue,编译器认得。
3.4 跑起来看结果:1、255、0 是怎么来的
按 F5 或者点顶上的绿色三角,程序跑起来。下拉框选 byte,三个框应该分别显示 1、255、0。选 int,显示 4、2147483647、-2147483648。选 decimal,显示 16,后面跟着一长串数字。
这时候回头看 2.2 那张分组表,你会发现它突然变得有意义了:为什么 byte 只到 255?因为它只有 1 个字节,8 位二进制,2 的 8 次方是 256,从 0 数到 255 正好用完。为什么 sbyte 是 -128 到 127?因为它把这 8 位分了一半给负数。为什么 int 是 4 字节?4 乘 8 等于 32 位,2 的 31 次方就是那个 2147483647。
这就是这个例子的价值:它把「取值范围」从一句要背的话,变成了你能亲眼看见的数字。以后你在代码里写byte b = 300;,脑子会自动闪一下「300 超过 255 了」,而不是等编译器报错才恍然大悟。
跑通之后可以自己改点东西练手:把SelectedIndex = 0改成 5,看看默认选项变成哪个;把某个 case 里的sizeof换成固定数字,看看界面有什么变化。改坏了不要紧,这几个文件都很小,重来一遍也就十分钟。
4. 第二个例子:类型转换,谁装谁不会溢出
4.1 隐式转换:小盒子往大盒子里倒
第一个例子解决的是「这个类型有多大」,第二个例子解决的是「类型之间怎么换」。为什么需要换?因为你从文本框读出来的永远是字符串,要算数就得转成数字;你算完的结果是 int,要塞进一个 short 变量里,也得转。
C# 把转换分成两类。第一类叫隐式转换,特点是编译器自动帮你做,不用写任何额外代码,而且绝对不会丢数据。它的规则特别简单:小范围往大范围转,安全,自动。
byte b = 200; int i = b; // 隐式转换,1 字节装进 4 字节,稳稳的 long l = i; // 再往上转,还是隐式 double d = l; // 整数转小数,也是隐式这四行一行注释都不用加,编译器全程放行。道理跟倒水一样:小杯子往大杯子里倒,怎么倒都不会洒。
原文那份实验里有一堆以imp开头的字符串数组,比如impbyte、impshort,它们记录的就是「谁能隐式转到谁」。看impbyte那个数组:{"short", "ushort", "int", "uint", "long", "ulong", "float", "double", "decimal"},读法是「byte 可以隐式转成后面这九个类型」。这些数组不是给你背的,是给下拉框填选项用的——你选了 byte,第二个下拉框就自动列出它所有能安全转过去的类型。
有一条容易忽略:char 也能隐式转成整数。char c = 'A'; int n = c;得到的是 65,因为字符在计算机里本来就是用数字存的。反过来 int 转 char 就得显式写,而且超出 0~65535 会出问题。
4.2 显式转换与 Convert 类:大盒子往小盒子倒会洒
第二类叫显式转换,也叫强制转换,特点是必须自己写括号,而且可能丢数据。规则反过来:大范围往小范围转,有风险,手动确认。
int i = 300; byte b = (byte)i; // 显式转换,结果不是 300,是 44为什么是 44?因为 byte 只有 8 位,300 的二进制是 100101100,九位。截断只留低 8 位,得到 00101100,也就是 44。这个过程叫溢出,而且编译器不会报错,它默默地给你一个错误答案。这是 C# 里最阴的坑之一,血泪经验:凡是看到强制转换的括号,就该条件反射地问一句「这个值会不会超」。
除了括号,C# 还提供Convert类,它做的也是显式转换,但行为不一样:溢出时它会抛异常,而不是默默截断。
int i = 300; // byte b = Convert.ToByte(i); // 这一行会抛 OverflowException string s = "123"; int n = Convert.ToInt32(s); // 字符串转数字,常用三个东西的区别记一下:(byte)i是截断,不报错但可能错;Convert.ToByte(i)是检查,超范围直接抛异常;byte.Parse("300")也是检查,但它只接受字符串输入。新手期我的建议是:能用 Convert 就用 Convert,让它在超范围的时候炸给你看,总比默默算错强。
4.3 一张表记住谁能装谁
下面这张表把常用类型的隐式转换方向列出来了,横着读:行头的类型能自动转到哪些类型。
| 源类型 | 能隐式转到 |
|---|---|
| sbyte | short、int、long、float、double、decimal |
| byte | short、ushort、int、uint、long、ulong、float、double、decimal |
| short | int、long、float、double、decimal |
| ushort | int、uint、long、ulong、float、double、decimal |
| int | long、float、double、decimal |
| uint | long、ulong、float、double、decimal |
| long | float、double、decimal |
| ulong | float、double、decimal |
| float | double |
| char | ushort、int、uint、long、ulong、float、double、decimal |
这张表不用背,用两次就熟了。规律是:同组内往字节更多的方向走,整数往小数走,有符号往无符号……最后这条是错的,反了。准确说是无符号往有符号的大类型走才安全,比如 uint 能转 long,但 uint 不能隐式转 int,因为 int 装不下 uint 的上半部分。
有个反直觉的点:long 能隐式转 float。明明 long 是 8 字节,float 才 4 字节,怎么还能自动转?因为这里比的不是字节数,是能表示的范围。float 虽然只有 4 字节,但它有指数位,能表示到 3.4×10 的 38 次方,比 long 的上限大得多,代价是精度会丢。所以这种转换是「不丢范围但可能丢精度」,编译器允许,你自己心里要有数。
4.4 把转换结果写到输出框:btnConvert_Click 骨架
回到工程,在解决方案里再新建一个 Windows 窗体项目,摆上两个 ComboBox(分别叫 cmbType 和 cmbConvertType)、一个输入 TextBox(tbInput)、一个输出 TextBox(tbOutput),再加一个按钮(btnConvert),文字写「转换」。第一个下拉框选源类型,第二个选目标类型,输入框填数字,点按钮,输出框出结果。
在类的最上面先放那几个数组,这里只列两个做示范,其余照着 4.3 那张表补:
// 放在 Form1 类里面、方法外面,作为字段 string[] impbyte = { "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "decimal" }; string[] impsbyte = { "short", "int", "long", "float", "double", "decimal" }; string[] impint = { "long", "float", "double", "decimal" };然后给 cmbType 的 SelectedIndexChanged 写代码,逻辑就一句:选了源类型之后,把第二个下拉框的选项换成它能转过去的那些。
private void cmbType_SelectedIndexChanged(object sender, EventArgs e) { if (cmbType.SelectedItem == null) return; string from = cmbType.SelectedItem.ToString(); cmbConvertType.Items.Clear(); // 每次换源类型都要清空,否则选项会越积越多 switch (from) { case "byte": cmbConvertType.Items.AddRange(impbyte); break; case "sbyte": cmbConvertType.Items.AddRange(impsbyte); break; case "int": cmbConvertType.Items.AddRange(impint); break; // 其余类型照着表补全 default: break; } }最关键的是按钮的点击事件,也就是原文作业里要求补全的部分。下面这个骨架能跑,也只实现了 byte 一条线,剩下的交给练习:
private void btnConvert_Click(object sender, EventArgs e) { if (cmbType.SelectedItem == null || cmbConvertType.SelectedItem == null) { MessageBox.Show("请先选择源类型和目标类型"); return; } string from = cmbType.SelectedItem.ToString(); string to = cmbConvertType.SelectedItem.ToString(); try { if (from == "byte") { // 先在源类型里安全落地,超范围这里就会抛异常 byte b = byte.Parse(tbInput.Text); switch (to) { case "short": tbOutput.Text = ((short)b).ToString(); break; case "ushort": tbOutput.Text = ((ushort)b).ToString(); break; case "int": tbOutput.Text = ((int)b).ToString(); break; case "long": tbOutput.Text = ((long)b).ToString(); break; case "float": tbOutput.Text = ((float)b).ToString(); break; case "double": tbOutput.Text = ((double)b).ToString(); break; default: tbOutput.Text = "这条还没实现"; break; } } else { tbOutput.Text = "这个源类型还没实现"; } } catch (FormatException) { MessageBox.Show("输入的不是合法数字"); } catch (OverflowException) { MessageBox.Show("输入超出 byte 的范围(0~255)"); } }这段代码里有两个地方值得说。一是try...catch,把可能出错的代码包起来,出错就跳到 catch 里给个提示,而不是让程序直接崩掉。FormatException对应「你输入了 abc」,OverflowException对应「你输入了 300」。二是为什么先转成 byte 再往外转——因为源类型是用户选的,你得先按他选的类型校验一遍,通过了再往目标类型转,顺序反了校验就失去意义。
写完之后按 F5,输入 200 选 byte 转 int,输出框应该显示 200。输入 300,应该弹「超出 byte 范围」。输入 abc,弹「不是合法数字」。这三条都通了,说明骨架是对的,剩下的就是把 switch 里的 case 一条条补完,那是纯体力活,也是最好的练习。
5. 新手最容易踩的 5 个坑:报错、溢出、选错值都在这
5.1 坑一:下拉框一选就报空引用
现象:程序刚启动,还没点下拉框,或者在代码里执行了Items.Clear(),立刻弹出「未将对象引用设置到对象的实例」。原因:SelectedIndexChanged这个事件在选项被清空时也会触发一次,这时候SelectedItem是 null,你直接对它调.ToString(),就炸了。解决:进入方法的第一行先判空,if (cmbDataType.SelectedItem == null) return;,一句话的事。
这个坑的教训不止于此。凡是「用户可能没选」的控件,读它值之前都要判空,包括 ComboBox、ListBox、DataGridView 的当前行。养成习惯之后,这类异常能少一大半。
5.2 坑二:byte 的最小值被记成 -128
现象:查询器跑起来,选 byte 显示最小值 0,选 sbyte 显示 -128,但有人写实验报告的时候把 byte 写成了 -128。原因:byte 和 sbyte 名字太像,一个无符号一个有符号,抄的时候串行了。解决:记住一个判断法——名字里带 u 或者本身就是 byte 这种「正数专用」的,最小值一定是 0;带 s 的(sbyte、short、signed)才管负数。实在不确定,把 3.4 那个程序跑一遍,让程序告诉你,别靠记忆。
顺带一提,原文实验里unint是错的,C# 里没有这个类型,正确写法是uint。这类拼写错误不会在写的时候报错,因为它在字符串里,只有运行时选到那一项才会发现下拉框里多了个不存在的类型、对应的 case 永远匹配不上。
5.3 坑三:字符串数组里写错一个字母,运行时才炸
现象:下拉框里有个选项点了没反应,三个文本框还是上一次的值,也没有任何报错。原因:strType数组里的名字和switch里的case标签不一致,比如数组写"Int",case 写"int"。C# 区分大小写,匹配不上就走 default,如果 default 里只写了MessageBox或者干脆没写,你看到的就只是「没反应」。解决:数组和 case 全部小写,别偷懒改大小写;default 分支里一定要给个明确提示,不要留空。
更稳的做法是干脆不用字符串。原文作业第 2 题问「如何用枚举类型替换字符串数组」,就是为了根治这个问题——枚举的成员是编译期检查的,写错立刻报红,不用等到运行。第 6 章会简单说一下怎么改。
5.4 坑四:转换前不判范围,程序直接抛异常
现象:转换器里输入 300 选 byte,程序没给结果,直接弹一个未处理异常,甚至整个程序退出。原因:只写了byte.Parse(tbInput.Text),没包 try/catch,Parse遇到超范围的值会抛OverflowException,没人接就往上冒。解决:两种写法选一种。要么用try...catch包住(4.4 的写法),要么在解析之前先判断:long tmp = long.Parse(text); if (tmp > byte.MaxValue || tmp < byte.MinValue) { 提示; return; }。后一种更啰嗦但流程更清楚,原文实验里就是用这种思路。
这里还有个隐藏坑:如果你用(byte)i这种强制转换,它不会抛异常,而是默默截断。300 变成 44,你还以为算对了。所以我的建议是,用户输入的东西一律用 Parse 或 Convert,别用括号硬转。
5.5 坑五:把 = 当成 ==,判断永远成立
现象:写了if (radioButton1.Checked = true),编译报错「无法将类型 bool 隐式转换为…」;或者写了if (x = 5),编译器居然放过了。原因:=是赋值,==才是比较。C# 里if (radioButton1.Checked = true)会被拦下来,因为 Checked 是只读属性;但如果是普通变量int x,if (x = 5)在 C# 里同样会被拦,因为 int 不能隐式转 bool。真正危险的是 bool 变量:if (isReady = true)编译器会放行,因为它把 true 赋给 isReady,整个表达式的值也是 true,永远进 if。解决:写判断的时候先写两个等号,或者干脆省略——if (isReady)和if (!isReady)比== true清楚得多,也少一个出错的地方。
这类错误最气人的地方是它不一定报错,只是逻辑不对,而且很难通过看代码发现。我第一次遇到的时候盯了半小时,最后是同事一句话点破的。
6. 下一步学什么:从字符串数组换到枚举,再往外走一步
跑通那两个例子之后,你已经摸到了 C# 的门。接下来别急着往深里钻,先把手上这份代码改好,改的过程比看十篇教程都有用。
第一个改法,把字符串数组换成枚举。原文作业第 2 题问的就是这个。做法是先在类外面定义一个枚举:
// 定义在 namespace 里面、class 外面 public enum DataType { Byte, SByte, Short, UShort, Int, UInt, Long, ULong, Float, Double, Decimal, Char, Bool }然后下拉框改成cmbDataType.DataSource = Enum.GetValues(typeof(DataType));,取值的时候用(DataType)cmbDataType.SelectedItem配合switch。好处立刻显现:写错成员名编译期就报红,不用等到点开下拉框才发现没反应;而且有了智能提示,你打一个Da它就列出来了,不用凭记忆敲。这个改动不大,但它是你从「照着抄」转向「知道为什么这么写」的第一步。
第二个改法,把转换器里那些imp数组也换成枚举列表,顺便把 switch 补全。13 个源类型乘上各自的转换目标,工作量不小,但每一行都是机械重复,正好用来练手速和熟练度。补完之后你会发现,真正需要动脑的只有 char 和 bool 这两个特殊类型,其余全是同一个套路。
再往后走,学习路径大概是这样:
| 阶段 | 该学什么 | 大概花多久 |
|---|---|---|
| 现在 | 数据类型、变量、转换、if/switch/循环 | 一到两周 |
| 下一步 | 类与对象、方法、属性、构造函数 | 两到三周 |
| 再下一步 | 集合(List、Dictionary)、异常处理、文件读写 | 三到四周 |
| 之后 | 数据库基础、界面数据绑定、小型项目实战 | 一个月以上 |
顺序不要乱跳。我见过不少人数据类型还没弄明白就去看 LINQ 和异步,结果每一步都在猜,猜错了也不知道错在哪,最后得出「C# 太难」的结论。其实不是难,是地基没打。
还有一个小建议:每次写完一段能跑的代码,故意改坏它一次。把sizeof(byte)改成sizeof(int)看看界面什么反应,把byte.MaxValue改成byte.MinValue看看结果对不对,把 try/catch 删掉输入 300 看看会弹什么。这种「主动制造错误」的练习,比一遍遍看正确代码更能让你记住边界在哪。
从那以后我每次教新人,都会让他们先把第一个例子的SelectedIndex = 0删掉,亲眼看看空引用长什么样——自己被炸过一次,比听别人讲十遍都管用。数据类型这一关,说到底就是不断在边界上试探,试到你闭着眼睛都知道 byte 到 255 就到头了,这关就算过了。希望帮到你。
本文还有配套的精品资源,点击获取