前文:
类型转换
在 C# 中,类型转换是将一个数据类型的值转换为另一个数据类型的过程,比如 C#中类型转换的“装箱”与“拆箱” ,类型转换可以分为两种:隐式类型转换 和 显式类型转换(也称为强制类型转换)。
隐式转换
隐式转换是不需要编写代码来指定的转换,编译器会自动进行。隐式转换是指将一个 较小范围 的数据类型转换为 较大范围 的数据类型时,编译器会自动完成类型转换,这些转换是 C# 默认的以安全方式进行的转换, 不会导致数据丢失。
例如,从 int
到 long
,从 float
到 double
等。从小的整数类型转换为大的整数类型,从派生类转换为基类。将一个 byte
类型的变量赋值给 int
类型的变量,编译器会自动将 byte
类型转换为 int
类型,不需要显示转换,例如:
byte b = 10;
int i = b; // 隐式转换,不需要显式转换
int intValue = 42;
long longValue = intValue; // 隐式转换,从 int 到 long
显式转换
显式类型转换,即强制类型转换,需要在代码中明确指定,是指将一个 较大范围 的数据类型转换为 较小范围 的数据类型时,或者将一个 对象类型 转换为 另一个对象 类型时,需要使用强制类型转换符号进行显示转换,强制转换会造成数据丢失。
例如,将一个 double
类型的变量赋值给 int
类型的变量,需要显示转换:
double doubleValue = 3.14;
int intValue = (int)doubleValue; // 强制从 double 到 int,数据可能损失小数部分
输出结果:
或者类似于:
int i = 10;
byte b = (byte)i; // 显式转换,需要使用强制类型转换符号
int intValue = 42;
float floatValue = (float)intValue; // 强制从 int 到 float,数据可能损失精度
int intValue = 123;
string stringValue = intValue.ToString(); // 将 int 转换为字符串
Convert转换方法
提供了下列内置的类型转换方法:
方法 | 描述 |
---|---|
ToBoolean | 如果可能的话,把类型转换为布尔型 |
ToByte | 把类型转换为字节类型 |
ToChar | 如果可能的话,把类型转换为单个 Unicode 字符类型 |
ToDateTime | 把类型(整数或字符串类型)转换为 日期-时间 结构 |
ToDecimal | 把浮点型或整数类型转换为十进制类型 |
ToDouble | 把类型转换为双精度浮点型 |
ToInt16/32/64 | 把类型转换为 16/32/64 位整数类型 |
ToSbyte | 把类型转换为有符号字节类型 |
ToSingle | 把类型转换为小浮点数类型 |
ToString | 把类型转换为字符串类型 |
ToType | 把类型转换为指定类型 |
ToUInt16/32/64 | 把类型转换为 16/32/64 位无符号整数类型 |
这些方法都定义在 System.Convert
类中,使用时需要包含 System
命名空间,它们提供了一种安全的方式来执行类型转换,因为它们可以处理 null
值,并且会抛出异常,如果转换不可能进行。例如,使用 Convert.ToInt32
方法将字符串转换为整数:
string str = "123";
int number = Convert.ToInt32(str); // 转换成功,number 为 123
如果字符串不是有效的整数表示,Convert.ToInt32
将抛出 FormatException
。
下面的实例把不同值的类型转换为字符串类型:
using System;
namespace TypeConversionApplication
{
class StringConversion
{
static void Main(string[] args)
{
// 定义一个整型变量
int i = 75;
// 定义一个浮点型变量
float f = 53.005f;
// 定义一个双精度浮点型变量
double d = 2345.7652;
// 定义一个布尔型变量
bool b = true;
// 两种方式
// string s = i.ToString();
// string s = Convert.ToString(i);
// 第一种实例方法,第二种是静态方法
// 第一种支持字符串格式化,第二种仅是简单的类型转换
// 将整型变量转换为字符串并输出
Console.WriteLine($"i.ToString: {i.ToString()}");
// 将浮点型变量转换为字符串并输出
Console.WriteLine($"f.ToString: {f.ToString()}");
// 将双精度浮点型变量转换为字符串并输出
string str1 = Convert.ToString(d);
Console.WriteLine($"d.ToString: {str1}");
// 将布尔型变量转换为字符串并输出
string str2 = Convert.ToString(b);
Console.WriteLine($"b.ToString: {str2}");
}
}
}
输出:
Parse转换方法
在前文中,System.Convert
类提供了一组静态方法,可以在各种基本 数据类型之间 进行转换,而 Parse
方法用于将 字符串(string) 转换为对应的 数值类型(value type) ,如果转换失败会 抛出异常。例如:
string str = "123.45";
double d = double.Parse(str);
还有一个 TryParse
方法,其类似于 Parse
,但它不会抛出异常,而是 返回一个布尔值 指示转换是否成功,例如:
string str = "123.45";
double d;
bool success = double.TryParse(str, out d);
if (success) {
Console.WriteLine($"转换成功: {d}");
} else {
Console.WriteLine("转换失败");
}
输出:
自定义类型转换
C# 还允许定义自定义类型转换操作,通过在类型中定义 implicit
或 explicit
关键字,例如:
using System;
public class Fahrenheit
{
public double Degrees { get; set; }
public Fahrenheit(double degrees)
{
Degrees = degrees;
}
// 隐式转换从Fahrenheit到double
public static implicit operator double(Fahrenheit f)
{
return f.Degrees;
}
// 显式转换从double到Fahrenheit
public static explicit operator Fahrenheit(double d)
{
return new Fahrenheit(d);
}
}
public class Program
{
public static void Main()
{
Fahrenheit f = new Fahrenheit(98.6);
Console.WriteLine("Fahrenheit object: " + f.Degrees + " degrees");
double temp = f; // 隐式转换
Console.WriteLine("After implicit conversion to double: " + temp + " degrees");
Fahrenheit newF = (Fahrenheit)temp; // 显式转换
Console.WriteLine("After explicit conversion back to Fahrenheit: " + newF.Degrees + " degrees");
}
}
定义了一个
Fahrenheit
类,并实现了从Fahrenheit
到double
的隐式转换和从double
到Fahrenheit
的显式转换。
输出:
Fahrenheit object: 98.6 degrees
After implicit conversion to double: 98.6 degrees
After explicit conversion back to Fahrenheit: 98.6 degrees
总结
在进行类型转换时需要注意以下几点:
- 隐式转换只能将较小范围的数据类型转换为较大范围的数据类型,不能将较大范围的数据类型转换为较小范围的数据类型;
- 显式转换可能会导致数据丢失或精度降低,需要进行数据类型的兼容性检查;
- 对于对象类型的转换,需要进行类型转换的兼容性检查和类型转换的安全性检查。
三种不同方法的列表如图:
图源自 菜鸟教程。
变量
一个变量只不过是一个供程序操作的 存储区 的 名字。在 C# 中,变量是用于存储和表示数据的标识符,在声明变量时,需要指定变量的类型,并且可以 选择性地 为其分配一个初始值,每个变量都有一个特定的类型,类型决定了变量的内存大小和布局,范围内的值可以存储在内存中,可以对变量进行一系列操作。
类型 | 举例 |
---|---|
整数类型 | sbyte、byte、short、ushort、int、uint、long、ulong 和 char |
浮点型 | float, double |
十进制类型 | decimal |
布尔类型 | true 或 false 值,指定的值 |
空字符串 | string |
空类型 | 可为空值的数据类型 |
示例:
int i, j, k;
char c, ch;
float f, salary;
double d;
// 或者初始化:
int i = 100;
// 赋值:
int i;
i = 100;
float
和double
的区别见:C#中float和double的区别
命名规则
- 变量名可以包含字母、数字和下划线。
- 变量名必须以字母或下划线开头。
- 变量名区分大小写。
- 避免使用 C# 的关键字作为变量名。
用户输入值
System
命名空间中的 Console
类提供了一个函数 ReadLine()
,用于接收来自用户的输入,并把它存储到一个变量中:
int num;
num = Convert.ToInt32(Console.ReadLine());
// 或者直接
int num = Convert.ToInt32(Console.ReadLine());
Console.ReadLine()
只接受 字符串 格式的数据,如果输入字符串的不是整数,则会报错,需要进行错误处理:
try
{
Console.Write("请输入一个整数: ");
int num = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("你输入的整数是: " + num);
}
catch (FormatException)
{
Console.WriteLine("输入的不是有效的整数!");
}
catch (OverflowException)
{
Console.WriteLine("输入的数值超出了 int 的范围!");
}
为了避免程序崩溃,建议使用 int.TryParse
方法,它可以安全地处理无效输入:
Console.Write("请输入一个整数: ");
string input_str = Console.ReadLine();
if (int.TryParse(input_str, out int num))
{
Console.WriteLine("你输入的整数是: " + num);
}
else
{
Console.WriteLine("输入的不是有效的整数!");
}
C# 中的两种表达式:
- Lvalue:lvalue 表达式可以出现在赋值语句的左边或右边。
- Rvalue:rvalue 表达式可以出现在赋值语句的右边,不能出现在赋值语句的左边。
变量是 lvalue 的,所以可以出现在赋值语句的左边。数值是 rvalue 的,因此不能被赋值,不能出现在赋值语句的左边。
作用域
变量的作用域定义了变量的可见性和生命周期,其通常由花括号 { }
定义的代码块来确定。
局部变量:
在方法、循环、条件语句等代码块内声明的变量是局部变量,它们只在声明它们的代码块中可见:
void MyMethod()
{
int localVar = 10; // 局部变量
// ...
}
// localVar 在这里不可见
块级作用域:
使用大括号 { }
创建的任何块都可以定义变量的作用域:
{
int blockVar = 20; // 块级作用域
// ...
}
// blockVar 在这里不可见
方法参数作用域:
方法的参数也有其自己的作用域,它们在整个方法中都是可见的:
void MyMethod(int parameter)
{
// parameter 在整个方法中可见
// ...
}
全局变量:
在类的成员级别定义的变量是成员变量,它们在整个类中可见,如果在命名空间级别定义,那么它们在整个命名空间中可见:
class MyClass
{
int memberVar = 30; // 成员变量,在整个类中可见
void TestFunc()
{
// memberVar 依然可见
}
}
常量
常量是 固定值,程序执行期间不会改变。常量可以是任何基本数据类型,比如整数常量、浮点常量、字符常量或者字符串常量,还有枚举常量。常量可以被当作常规的变量,只是它们的 值在定义后不能被修改。
整数常量
整数常量可以是 十进制、八进制 或 十六进制 的常量,前缀 指定基数:0x
或 0X
表示十六进制,0
表示八进制,没有前缀 则表示十进制。
后缀 指定基数:可以是 U
和 L
的组合,其中,U
和 L
分别表示 unsigned
和 long
,后缀可以是大写或者小写,多个后缀以 任意顺序 进行组合。例如:
后缀基数在浮点类型的常量中也可以使用。
212 // 合法
215u // 合法
0xFeeL // 合法
078 // 非法:8 不是一个八进制数字
032UU // 非法:不能重复后缀
85 // 十进制
0213 // 八进制
0x4b // 十六进制
30 // int
30u // 无符号 int
30l // long
30ul // 无符号 long
浮点常量
一个浮点常量是由整数部分、小数点、小数部分和指数部分组成,可以使用小数形式或者指数形式来表示浮点常量,使用小数形式表示时,必须包含小数点、指数或同时包含两者,使用指数形式表示时,必须包含整数部分、小数部分或同时包含两者。有符号的指数是用 e
或 E
表示的。例如:
3.14159 // 合法
314159E-5L // 合法
510E // 非法:不完全指数
210f // 非法:没有小数或指数
.e55 // 非法:缺少整数或小数
字符常量
字符常量是括在单引号里,例如 'x'
,且可存储在一个简单的字符类型变量中。
一个字符常量可以是一个普通字符(例如 'x'
)、一个转义序列(例如 '\t'
)或者一个通用字符(例如 '\u02C0'
)。
在 C# 中有一些特定的字符,当它们的前面带有 反斜杠 时有特殊的意义,可用于表示换行符( \n
)或制表符 tab( \t
)。
图源自 菜鸟教程。
字符串常量
字符串常量是括在双引号 " "
或者 @" "
里。字符串常量包含的字符与字符常量相似,可以是:普通字符、转义序列和通用字符,使用字符串常量时,可以把一个很长的行拆成多个行,可以使用空格分隔各个部分,例如:
string a = "hello, world"; // hello, world
string b = @"hello, world"; // hello, world
string c = "hello \t world"; // hello world
string d = @"hello \t world"; // hello \t world
string e = "Joe said \"Hello\" to me"; // Joe said "Hello" to me
string f = @"Joe said ""Hello"" to me"; // Joe said "Hello" to me
string g = "\\\\server\\share\\file.txt"; // \\server\share\file.txt
string h = @"\\server\share\file.txt"; // \\server\share\file.txt
string i = "one\r\ntwo\r\nthree";
string j = @"one
two
three";
定义常量
常量是使用 const
关键字来定义的,用法:
const <data_type> <constant_name> = value;
示例:
using System;
public class ConstTest
{
class SampleClass
{
// x,y,c1,c2 为类 SampleClass 的成员
public int x;
public int y;
public const int c1 = 5;
public const int c2 = c1 + 5; // c2 = 10
public SampleClass(int p1, int p2)
{
x = p1;
y = p2;
}
}
static void Main()
{
// 把 11 赋给 mC.x,22 赋给 mC.y
SampleClass mC = new SampleClass(11, 22);
// 打印变量 x,y 的值
Console.WriteLine($"x = {mC.X}, y = {mC.Y}");
// 打印常量 c1,c2 的值
Console.WriteLine($"c1 = {SampleClass.C1}, c2 = {SampleClass.C2}");
}
}
和类同名的函数叫 构造函数,不需要指定返回类型,详见 C#中和类同名的构造函数
输出: