一、变量类型转换:隐式与显式的门道
(一)隐式转换:编译器的 “贴心小助手”
隐式转换是编译器自动进行的类型转换,无需开发者手动干预。这种转换通常发生在将取值范围小的数据类型赋值给取值范围大的数据类型时,就像把小盒子里的东西放进大盒子,完全没问题。例如:
short num1 = 100;
int num2 = num1;
在这个例子中,short
类型的num1
可以直接赋值给int
类型的num2
,因为int
类型的取值范围比short
大,能够容纳short
类型的值。常见的隐式转换关系有:byte
可以转换为short
、ushort
、int
、uint
、long
、ulong
、float
、double
、decimal
等;short
可以转换为int
、long
、float
、double
、decimal
等。
(二)显式转换:手动掌控数据 “乾坤大挪移”
当需要把取值范围大的数据类型赋值给取值范围小的数据类型时,就像把大盒子里的东西放进小盒子,可能会出现装不下的情况,这时就需要显式转换。显式转换需要开发者明确告诉编译器要进行类型转换,使用(目标类型)
的语法。例如:
int bigNumber = 30000;
short smallNumber = (short)bigNumber;
不过要注意,显式转换可能会导致数据丢失或精度降低。比如上面的例子,如果bigNumber
的值超出了short
类型的取值范围,转换后得到的smallNumber
的值可能就不是预期的了。
(三)使用Convert
类进行特殊转换
除了基本的隐式和显式转换,在处理数字字符串和数字类型之间的转换时,Convert
类提供了非常实用的方法。比如将字符串转换为整数:
string numberStr = "123";
int number = Convert.ToInt32(numberStr);
Convert
类还提供了ToDouble
、ToFloat
、ToDecimal
等方法,可以满足不同类型之间的转换需求。但在使用时要确保字符串内容是有效的数字格式,否则会抛出异常。例如,将非数字字符串"abc"
转换为数字就会出错。
二、复杂变量类型:拓展编程的边界
(一)枚举类型:让代码表意更清晰
在编程中,我们经常会遇到一些具有固定取值集合的数据。比如在游戏开发中,游戏角色的状态可能有 “站立”“奔跑”“跳跃”“死亡” 等。如果使用普通的整数来表示这些状态,代码的可读性会很差,而且容易出错。这时候枚举类型就派上用场了。
枚举类型的定义格式如下:
enum GameCharacterState
{
Standing,
Running,
Jumping,
Dead
}
使用枚举类型声明变量并赋值:
GameCharacterState state = GameCharacterState.Running;
枚举类型中的每个值默认是int
类型,并且从 0 开始依次递增。也可以手动指定每个枚举值对应的整数值:
enum Weekday
{
Monday = 1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
在这个例子中,Monday
被指定为 1,后续的枚举值会依次递增。枚举类型在提高代码可读性和可维护性方面有着显著的优势,在大型项目中,合理使用枚举能让代码逻辑更加清晰。
(二)结构:整合相关数据的 “收纳盒”
结构是一种可以将多个不同类型的数据组合在一起的数据类型。当我们需要表示一个具有多个相关属性的对象时,结构就非常有用。比如表示一个二维点的坐标,需要横坐标和纵坐标:
struct Point2D
{
public int X;
public int Y;
}
这里使用struct
关键字定义了Point2D
结构,它包含两个int
类型的成员X
和Y
。声明和使用结构变量:
Point2D point = new Point2D();
point.X = 10;
point.Y = 20;
也可以在声明时初始化结构变量:
Point2D anotherPoint = new Point2D { X = 5, Y = 8 };
结构在内存分配和使用上有其特点,它是值类型,存储在栈上,相比于引用类型,在处理小型、简单的数据集合时,具有更高的效率。
(三)数组:批量存储数据的 “集装箱”
数组是用于存储多个相同类型数据的集合。在实际编程中,当需要处理大量同类型数据时,数组能极大地简化代码。例如,存储一个班级学生的考试成绩:
int[] scores = new int[30];
这里声明了一个包含 30 个元素的int
类型数组scores
,数组元素默认初始化为 0。数组的初始化方式有多种,除了上述方式,还可以直接初始化元素值:
int[] scores = { 85, 90, 78, 88, 92 };
或者在声明时指定大小并初始化:
int[] scores = new int[5] { 85, 90, 78, 88, 92 };
访问数组元素通过索引进行,索引从 0 开始。例如,获取数组中第一个元素的值:
int firstScore = scores[0];
遍历数组是常见的操作,有多种方式可以实现:
for
循环遍历:
int[] numbers = { 1, 2, 3, 4, 5 };
for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine(numbers[i]);
}
while
循环遍历:
int[] numbers = { 1, 2, 3, 4, 5 };
int i = 0;
while (i < numbers.Length)
{
Console.WriteLine(numbers[i]);
i++;
}
foreach
循环遍历:
int[] numbers = { 1, 2, 3, 4, 5 };
foreach (int number in numbers)
{
Console.WriteLine(number);
}
foreach
循环适用于只需要读取数组元素的场景,它更加简洁,但不能直接修改数组元素的值。如果需要在遍历过程中修改元素值,还是要使用for
或while
循环。
数组还支持多维数组,比如二维数组可以用来表示矩阵:
int[,] matrix = new int[3, 3] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
访问二维数组元素时需要指定两个索引:
int element = matrix[1, 1];
三、字符串:特殊的 “字符数组”
在 C# 中,字符串可以看作是字符char
类型的数组,这使得我们可以像操作数组一样操作字符串。例如,获取字符串中指定位置的字符:
string name = "Alice";
char firstChar = name[0];
通过Length
属性可以获取字符串的长度:
int nameLength = name.Length;
(一)字符串遍历与反转
遍历字符串的每个字符可以使用循环,就像遍历数组一样。下面是使用for
循环反向输出字符串的示例:
string input = "Hello, World!";
for (int i = input.Length - 1; i >= 0; i--)
{
Console.Write(input[i]);
}
(二)字符串常用方法
1.大小写转换:ToLower()
方法将字符串转换为小写,ToUpper()
方法将字符串转换为大写。
string original = "Hello, World!";
string lowerCase = original.ToLower();
string upperCase = original.ToUpper();
2.去除空白字符:Trim()
方法去除字符串两端的空白字符,TrimStart()
去除字符串开头的空白字符,TrimEnd()
去除字符串结尾的空白字符。
string strWithSpaces = " Hello, World! ";
string trimmed = strWithSpaces.Trim();
string trimmedStart = strWithSpaces.TrimStart();
string trimmedEnd = strWithSpaces.TrimEnd();
3.分割字符串:Split()
方法根据指定的分隔符将字符串分割成字符串数组。
string sentence = "Apple, Banana, Orange";
string[] fruits = sentence.Split(',');
四.题目
1. 寻找 100 到 999 之间的水仙花数
题目改编:在 100 到 999 这个范围内,找出所有满足 “水仙花数” 条件的数字。水仙花数是指一个三位数,其各位数字的立方和等于该数本身,例如 153 = 1³ + 5³ + 3³。
using System;
class NarcissisticNumbers
{
static void Main()
{
for (int num = 100; num < 1000; num++)
{
int hundreds = num / 100;
int tens = (num / 10) % 10;
int units = num % 10;
if (Math.Pow(hundreds, 3) + Math.Pow(tens, 3) + Math.Pow(units, 3) == num)
{
Console.WriteLine(num);
}
}
}
}
代码解释:
- 使用
for
循环遍历 100 到 999 之间的所有数字。 - 对于每个数字,通过除法和取模运算分别获取其百位、十位和个位数字。
- 计算各位数字的立方和,并与该数字本身进行比较,如果相等则输出该数字。
2. 可乐兑换问题
题目改编:已知 3 个可乐空瓶可以换一瓶可乐,现在初始有 364 瓶可乐。请计算总共可以喝到多少瓶可乐,以及最后会剩下多少个空瓶。
using System;
class ColaExchange
{
static void Main()
{
int totalCola = 364;
int emptyBottles = totalCola;
while (emptyBottles >= 3)
{
int exchangedCola = emptyBottles / 3;
totalCola += exchangedCola;
emptyBottles = emptyBottles % 3 + exchangedCola;
}
Console.WriteLine($"总共可以喝到 {totalCola} 瓶可乐,剩下 {emptyBottles} 个空瓶。");
}
}
代码解释:
- 初始化
totalCola
为初始可乐数量,emptyBottles
也初始化为初始可乐数量。 - 使用
while
循环,只要空瓶数量大于等于 3,就进行兑换操作。 - 计算可兑换的可乐数量
exchangedCola
,并更新totalCola
和emptyBottles
的值。 - 最后输出总共喝到的可乐数量和剩余的空瓶数量。
3. 猜数字游戏
题目改编:计算机随机生成一个 0 到 50 之间的整数,玩家需要猜测这个数字。每次猜测后,计算机根据玩家的猜测结果给出提示(猜大了或猜小了),直到玩家猜对为止。
using System;
class GuessNumberGame
{
static void Main()
{
Random random = new Random();
int secretNumber = random.Next(0, 51);
int guess;
do
{
Console.Write("请您输入一个 0 - 50 之间的数:");
guess = int.Parse(Console.ReadLine());
if (guess < secretNumber)
{
Console.WriteLine($"您猜小了,这个数字比 {guess} 大。");
}
else if (guess > secretNumber)
{
Console.WriteLine($"您猜大了,这个数字比 {guess} 小。");
}
else
{
Console.WriteLine($"恭喜您猜对了,这个数字为:{secretNumber}");
}
} while (guess != secretNumber);
}
}
代码解释:
- 使用
Random
类生成一个 0 到 50 之间的随机整数secretNumber
。 - 使用
do-while
循环,不断提示玩家输入猜测的数字。 - 根据玩家的猜测结果给出相应的提示,直到猜对为止。
4. 字符串加密程序
题目改编:编写一个应用程序,对用户输入的字符串进行加密。对于字母字符,按照特定规则进行加密:小写字母 'a' 到 'z' 循环后移 3 位,大写字母 'A' 到 'Z' 也循环后移 3 位,其他字符保持不变。
using System;
class StringEncryption
{
static void Main()
{
Console.Write("请输入要加密的字符串:");
string input = Console.ReadLine();
string encrypted = "";
foreach (char c in input)
{
if (char.IsLower(c))
{
encrypted += (char)(((c - 'a' + 3) % 26) + 'a');
}
else if (char.IsUpper(c))
{
encrypted += (char)(((c - 'A' + 3) % 26) + 'A');
}
else
{
encrypted += c;
}
}
Console.WriteLine($"加密后的字符串为:{encrypted}");
}
}
代码解释:
- 读取用户输入的字符串。
- 遍历字符串中的每个字符,判断其是否为小写字母或大写字母。
- 对于字母字符,通过计算循环后移 3 位的位置进行加密,其他字符直接添加到加密后的字符串中。
- 最后输出加密后的字符串。
5. 数字排序程序
题目改编:编写一个控制台程序,要求用户输入一组用空格分隔的数字,然后使用 Array.Sort
方法和冒泡排序算法分别对这些数字进行从小到大排序,并输出排序结果。
using System;
class NumberSorting
{
static void Main()
{
Console.Write("请输入一组用空格分隔的数字:");
string input = Console.ReadLine();
string[] numbersStr = input.Split(' ');
int[] numbers = new int[numbersStr.Length];
for (int i = 0; i < numbersStr.Length; i++)
{
numbers[i] = int.Parse(numbersStr[i]);
}
// 使用 Array.Sort 方法排序
int[] sortedByArraySort = (int[])numbers.Clone();
Array.Sort(sortedByArraySort);
Console.WriteLine("使用 Array.Sort 方法排序后的结果:");
foreach (int num in sortedByArraySort)
{
Console.Write(num + " ");
}
Console.WriteLine();
// 使用冒泡排序
int[] sortedByBubbleSort = (int[])numbers.Clone();
for (int i = 0; i < sortedByBubbleSort.Length - 1; i++)
{
for (int j = 0; j < sortedByBubbleSort.Length - i - 1; j++)
{
if (sortedByBubbleSort[j] > sortedByBubbleSort[j + 1])
{
int temp = sortedByBubbleSort[j];
sortedByBubbleSort[j] = sortedByBubbleSort[j + 1];
sortedByBubbleSort[j + 1] = temp;
}
}
}
Console.WriteLine("使用冒泡排序后的结果:");
foreach (int num in sortedByBubbleSort)
{
Console.Write(num + " ");
}
Console.WriteLine();
}
}
代码解释:
- 读取用户输入的字符串,并使用
Split
方法将其按空格分割成字符串数组。 - 将字符串数组转换为整数数组。
- 克隆整数数组,分别使用
Array.Sort
方法和冒泡排序算法对克隆数组进行排序。 - 输出两种排序方法的结果。
6. 蟠桃数量计算问题
题目改编:悟空吃蟠桃,第一天吃掉桃子总数一半多一个,第二天又将剩下的桃子吃掉一半多一个,以后每天都吃掉前一天剩下的一半多一个,到第 n
天准备吃的时候只剩下一个桃子。请根据用户输入的 n
值,计算悟空第一天开始吃的时候桃子一共有多少个。
using System;
class PeachCalculation
{
static void Main()
{
Console.Write("请输入天数 n:");
int n = int.Parse(Console.ReadLine());
int peaches = 1;
for (int i = n - 1; i > 0; i--)
{
peaches = (peaches + 1) * 2;
}
Console.WriteLine($"第一天开始吃的时候桃子一共有 {peaches} 个。");
}
}
代码解释:
- 读取用户输入的天数
n
。 - 从第
n
天剩下的 1 个桃子开始,逆向推导每天开始时的桃子数量。 - 使用
for
循环,根据每天吃桃子的规则计算前一天的桃子数量。 - 最后输出第一天开始时的桃子数量。
7. 最小数交换问题
题目改编:用户输入 n
(n < 100
)个整数,找出其中最小的数,将它与数组的第一个数交换位置,然后输出交换后的数组。
using System;
class MinNumberSwap
{
static void Main()
{
Console.Write("请输入数字的个数(n < 100):");
int n = int.Parse(Console.ReadLine());
int[] numbers = new int[n];
Console.Write("请输入这 {0} 个数字:", n);
string[] input = Console.ReadLine().Split(' ');
for (int i = 0; i < n; i++)
{
numbers[i] = int.Parse(input[i]);
}
int minIndex = 0;
for (int i = 1; i < n; i++)
{
if (numbers[i] < numbers[minIndex])
{
minIndex = i;
}
}
int temp = numbers[0];
numbers[0] = numbers[minIndex];
numbers[minIndex] = temp;
Console.WriteLine("交换后的数组为:");
foreach (int num in numbers)
{
Console.Write(num + " ");
}
Console.WriteLine();
}
}
代码解释
- 读取用户输入的数字个数
n
,并创建一个长度为n
的整数数组。 - 读取用户输入的
n
个数字,并存储到数组中。 - 遍历数组,找出最小数的索引
minIndex
。 - 交换最小数和数组第一个数的位置。
- 输出交换后的数组。
8. 有序数组插入问题
题目改编:有 n
(n <= 100
)个整数已经按照从小到大的顺序排列好,现在用户输入一个整数 x
,请将该数插入到序列中,并使新的序列仍然有序,最后输出新的序列。
using System;
class OrderedArrayInsertion
{
static void Main()
{
Console.Write("请输入有序数组的长度(n <= 100):");
int n = int.Parse(Console.ReadLine());
int[] array = new int[n + 1];
Console.Write("请输入有序数组的元素(从小到大):");
string[] input = Console.ReadLine().Split(' ');
for (int i = 0; i < n; i++)
{
array[i] = int.Parse(input[i]);
}
Console.Write("请输入要插入的整数 x:");
int x = int.Parse(Console.ReadLine());
int insertIndex = n;
for (int i = 0; i < n; i++)
{
if (x < array[i])
{
insertIndex = i;
break;
}
}
for (int i = n; i > insertIndex; i--)
{
array[i] = array[i - 1];
}
array[insertIndex] = x;
Console.WriteLine("插入后的新序列为:");
foreach (int num in array)
{
Console.Write(num + " ");
}
Console.WriteLine();
}
}
代码解释:
- 读取用户输入的有序数组长度
n
,并创建一个长度为n + 1
的整数数组。 - 读取用户输入的有序数组元素,并存储到数组的前
n
个位置。 - 读取用户输入的要插入的整数
x
。 - 遍历数组,找到
x
应该插入的位置insertIndex
。 - 将插入位置之后的元素依次后移一位。
- 将
x
插入到insertIndex
位置。 - 输出插入后的新序列。
9. 工资发零问题
题目改编:泰课的老师每月 8 号发工资,财务处的小云老师需要考虑最少准备多少张人民币,才能在给每位老师发工资时都不用老师找零。假设老师的工资都是正整数,单位为元,人民币有 100 元、50 元、10 元、5 元、2 元和 1 元六种。
using System;
class SalaryPayment
{
static void Main()
{
Console.Write("请输入老师的工资:");
int salary = int.Parse(Console.ReadLine());
int hundredCount = salary / 100;
salary %= 100;
int fiftyCount = salary / 50;
salary %= 50;
int tenCount = salary / 10;
salary %= 10;
int fiveCount = salary / 5;
salary %= 5;
int twoCount = salary / 2;
salary %= 2;
int oneCount = salary;
int totalNotes = hundredCount + fiftyCount + tenCount + fiveCount + twoCount + oneCount;
Console.WriteLine($"最少需要准备 {totalNotes} 张人民币。");
Console.WriteLine($"100 元:{hundredCount} 张,50 元:{fiftyCount} 张,10 元:{tenCount} 张,5 元:{fiveCount} 张,2 元:{twoCount} 张,1 元:{oneCount} 张。");
}
}
代码解释:
- 读取用户输入的老师工资。
- 依次计算需要的 100 元、50 元、10 元、5 元、2 元和 1 元人民币的张数。
- 计算总共需要的人民币张数。
- 输出最少需要准备的人民币张数以及每种面值的张数。
10. 合法标识符判断问题
题目改编:编写一个程序,判断用户输入的字符串是否为 C# 的合法标识符。合法标识符必须以字母、下划线或 @ 开头,后面可以跟字母、数字、下划线。
using System;
using System.Text.RegularExpressions;
class IdentifierValidation
{
static void Main()
{
Console.Write("请输入一个字符串:");
string input = Console.ReadLine();
bool isValid = Regex.IsMatch(input, @"^[@_a-zA-Z][a-zA-Z0-9_]*$");
if (isValid)
{
Console.WriteLine("该字符串是 C# 的合法标识符。");
}
else
{
Console.WriteLine("该字符串不是 C# 的合法标识符。");
}
}
}
代码解释:
- 读取用户输入的字符串。
- 使用正则表达式
^[@_a-zA-Z][a-zA-Z0-9_]*$
判断字符串是否符合 C# 合法标识符的规则。 - 根据判断结果输出相应信息。
11. 回文串判断问题
题目改编:编写一个程序,判断用户输入的字符串是否为回文串。回文串是指正读和反读都一样的字符串。
using System;
class PalindromeCheck
{
static void Main()
{
Console.Write("请输入一个字符串:");
string input = Console.ReadLine();
string reversed = ReverseString(input);
if (input == reversed)
{
Console.WriteLine("该字符串是回文串。");
}
else
{
Console.WriteLine("该字符串不是回文串。");
}
}
static string ReverseString(string s)
{
char[] charArray = s.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
}
代码解释:
- 读取用户输入的字符串。
- 定义
ReverseString
方法,用于反转字符串
五.结语
通过对 C# 变量的深入学习,无论是基础的类型转换,还是复杂的变量类型,以及特殊的字符串处理,我们都掌握了更多编程的 “利器”。希望大家在实际编程中不断实践,灵活运用这些知识,编写出更优秀的代码。