C#基础-2-类型转换、变量与常量

前文:

C#基础-1-标识符与数据类型

类型转换

在 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# 还允许定义自定义类型转换操作,通过在类型中定义 implicitexplicit 关键字,例如:

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 类,并实现了从 Fahrenheitdouble 的隐式转换和从 doubleFahrenheit 的显式转换。

输出:

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#中和类同名的构造函数

输出: