目录
C# 数据类型
值类型(Value types)
引用类型(Reference types)
指针类型(Pointer types)
C# 类型转换
隐式类型转换
显式类型转换
C# 类型转换方法
值类型(Value types)在C#中是直接包含其数据的类型,它们是从类 System.ValueType 中派生的。这些类型的实例被存储在栈上,而不是在堆上,这使得它们的访问速度更快,但通常也限制了它们的大小和生存期。
下表列出了 C# 可用的值类型:
类型 | 描述 | 范围 | 默认值 |
---|---|---|---|
bool | 布尔值 | True 或 False | False |
byte | 8 位无符号整数 | 0 到 255 | 0 |
char | 16 位 Unicode 字符 | U +0000 到 U +ffff | '\0' |
decimal | 128 位精确的十进制值,28-29 有效位数 | (-7.9 x 1028 到 7.9 x 1028) / 100 到 28 | 0.0M |
double | 64 位双精度浮点型 | (+/-)5.0 x 10-324 到 (+/-)1.7 x 10308 | 0.0D |
float | 32 位单精度浮点型 | -3.4 x 1038 到 + 3.4 x 1038 | 0.0F |
int | 32 位有符号整数类型 | -2,147,483,648 到 2,147,483,647 | 0 |
long | 64 位有符号整数类型 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 | 0L |
sbyte | 8 位有符号整数类型 | -128 到 127 | 0 |
short | 16 位有符号整数类型 | -32,768 到 32,767 | 0 |
uint | 32 位无符号整数类型 | 0 到 4,294,967,295 | 0 |
ulong | 64 位无符号整数类型 | 0 到 18,446,744,073,709,551,615 | 0 |
ushort | 16 位无符号整数类型 | 0 到 65,535 | 0 |
注意:使用sizeof方法可以得到一个类型或一个变量在特定平台上的准确尺寸。表达式sizeof(type)会产生以字节为单位存储对象或类型的存储尺寸。这在需要精确控制内存占用或进行与底层平台相关的操作时非常有用。
using System;
class Program
{
static void Main()
{
// 声明和初始化不同的值类型变量
int intValue = 10;
bool boolValue = true;
char charValue = 'A';
double doubleValue = 3.14;
// 输出这些值
Console.WriteLine("int值: " + intValue);
Console.WriteLine("布尔值: " + boolValue);
Console.WriteLine("char值: " + charValue);
Console.WriteLine("double值: " + doubleValue);
// 使用sizeof方法获取类型占用的内存大小
Console.WriteLine("int的大小: " + sizeof(int));
Console.WriteLine("bool的大小: " + sizeof(bool));
Console.WriteLine("char的大小: " + sizeof(char));
Console.WriteLine("double的大小: " + sizeof(double));
// 使用默认值初始化变量
int defaultValueInt = default(int);
bool defaultValueBool = default(bool);
char defaultValueChar = default(char);
double defaultValueDouble = default(double);
// 输出默认值
Console.WriteLine("int的默认值: " + defaultValueInt);
Console.WriteLine("bool的默认值: " + defaultValueBool);
Console.WriteLine("char的默认值: " + (int)defaultValueChar); // 强制转换为int以显示数字
Console.WriteLine("double的默认值: " + defaultValueDouble);
}
}
以上示例中,我们声明和初始化了几种不同类型的值类型变量,然后输出它们的值。接着使用sizeof方法获取了各类型变量所占用的内存大小,并演示了使用default关键字来初始化变量的默认值。
总的来说,值类型在C#中扮演着重要的角色,它们的运行效率高、易于使用,并且具有明确的大小和默认值。这些特性使得值类型在编程中被广泛应用。
在C#中,引用类型确实不直接包含存储在变量中的实际数据,而是包含对变量的引用,这个引用指向内存中存储的实际数据。
在C#中,常见的内置引用类型包括:
1、object:
在C#中,对象(Object)类型是所有其他数据类型的根类型,也称为通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。Object 是 System.Object 类的别名,在 .NET 中它是最基本的类型。
对象(Object)类型可以持有任何类型的值,包括值类型、引用类型、预定义类型或用户自定义类型。但是,在将值分配给对象类型之前,需要进行显式或隐式的类型转换。当将值类型转换为对象类型时,这个过程被称为装箱(boxing);而当对象类型转换为值类型时,则被称为拆箱(unboxing)。
装箱和拆箱是因为值类型和引用类型在内存中存储的方式不同,装箱将值类型封装为对象,而拆箱则是从对象中提取值类型的过程。这些操作需要一定的系统开销,因此在性能要求较高的情况下,需要注意避免过多的装箱和拆箱操作。
下面是一个简单的C#代码示例:
using System;
class Program
{
static void Main()
{
// 值类型转换为对象类型(装箱)
int value = 42;
object obj = value; // 装箱操作,将值类型int装箱为object
// 对象类型转换为值类型(拆箱)
int result = (int)obj; // 拆箱操作,将object拆箱为int
Console.WriteLine(result); // 输出结果为 42
}
}
在这个示例中,我们首先创建了一个值类型的整数变量 value,并将其赋值为 42。然后通过将 value 赋值给一个 object 类型的变量 obj,发生了装箱操作。接着,我们又通过显式的类型转换,将 obj 中的值拆箱为一个新的整数变量 result。最终输出 result 的值,应该为 42。
2、dynamic:
在C#中,可以使用关键字 dynamic 来声明动态类型的变量。动态类型的变量可以存储任何类型的值,并且类型检查是在运行时而不是编译时进行的。
声明动态类型的语法如下:
dynamic dynamicVariable;
通过上面的语法,你可以声明一个名为 dynamicVariable 的动态类型变量。然后可以将任何类型的值赋给这个变量,而不需要在编译时知道具体的类型。
下面是一个简单的示例,演示了动态类型的使用:
using System;
class Program
{
static void Main()
{
dynamic dynamicVariable;
dynamicVariable = 10; // 可以存储整数值
Console.WriteLine(dynamicVariable);
dynamicVariable = "Hello, dynamic!"; // 也可以存储字符串值
Console.WriteLine(dynamicVariable);
dynamicVariable = new { Name = "Jerry", Age = 30 }; // 甚至可以存储匿名类型
Console.WriteLine(dynamicVariable.Name + " is " + dynamicVariable.Age + " years old");
}
}
在这个示例中,我们声明了一个 dynamic 类型的变量 dynamicVariable,然后依次将整数、字符串和匿名类型赋给它,而不需要提前知道具体的类型。当我们输出 dynamicVariable 时,它能够正确地展示相应的值。
3、string:
字符串(String)类型在C#中允许我们给变量分配任何字符串值。字符串类型实际上是 System.String 类的别名,它是从对象(Object)类型派生的。
在C#中,我们可以使用引号(" ")或者 @引号(@" ")来分配字符串值。例如:
string str1 = "Hello, World!"; // 使用双引号分配字符串值
string str2 = @"C:\Program Files\"; // 使用@引号分配字符串值,@符号可以用来忽略转义字符
在这里,str1 和 str2 都是字符串类型的变量,分别分配了不同的字符串值。
另外,字符串类型还支持丰富的字符串操作和方法,比如连接字符串、获取子串、转换大小写等。例如:
string firstName = "John";
string lastName = "Doe";
string fullName = firstName + " " + lastName; // 使用加号连接字符串
string upperCaseName = fullName.ToUpper(); // 将字符串转换为大写
string lowerCaseName = fullName.ToLower(); // 将字符串转换为小写
在C#中,指针类型(Pointer types)允许程序直接操作内存地址,从而实现更灵活和高效的编程。然而,需要注意的是,指针类型在C#中属于不安全代码,因此需要特殊的权限和注意事项来使用。
指针类型的语法如下:
type* identifier;
其中,type 是指针指向的数据类型,identifier 是指针变量的名称。
下面是一个简单的示例,演示了指针类型的基本用法:
unsafe class Program
{
static void Main()
{
int number = 10;
int* pointer = &number; // 获取变量 number 的地址并赋值给指针变量 pointer
Console.WriteLine("数字的值: " + number);
Console.WriteLine("号码地址: " + (long)pointer);
Console.WriteLine("地址处的值: " + *pointer); // 通过指针访问内存地址中的值
}
}
在这个示例中,我们使用了 unsafe 关键字来定义包含指针的代码块。然后声明了一个整型变量 number 和一个整型指针变量 pointer。通过 & 运算符获取了 number 变量的地址,并将其赋给指针变量。最后通过 * 运算符可以访问指针所指向地址的值。
需要注意的是,为了使用指针类型,需要在程序或方法中标记为 unsafe,并且需要在项目属性中启用“允许不安全代码”选项。同时,在使用指针类型时,需要格外小心,因为直接操作内存地址可能导致程序运行时出现难以察觉的错误。
总的来说,指针类型在C#中属于不常用且高级的特性,一般情况下可以通过引用类型等方式来实现相同的功能。
在 C# 中,类型转换是将一个数据类型的值转换为另一个数据类型的过程。
C# 中的类型转换可以分为两种:隐式类型转换和显式类型转换(也称为强制类型转换)。
在C#中,隐式类型转换是指将一个较小范围的数据类型转换为一个较大范围的数据类型时,编译器会自动完成类型转换,而且这些转换是以安全方式进行的,不会导致数据丢失。这种转换是默认的行为,开发人员无需显式声明转换操作符。
例如,将一个整数转换为长整型、将一个浮点数转换为双精度浮点型,这些都属于隐式类型转换,因为没有显式地调用转换操作符,编译器会在必要的时候自动进行转换。
这种隐式类型转换的特性有助于简化代码,并且在不会导致数据丢失的情况下提供了方便。但需要注意的是,如果进行的是从较大范围的数据类型到较小范围的数据类型的转换,那么就需要使用显式类型转换,并且需要小心处理可能产生的数据损失。
总之,C#的隐式类型转换使得编码变得更加便捷,同时又保证了数据的安全性和完整性。
以下是一个关于隐式类型转换的示例代码:
using System;
class Program
{
static void Main()
{
int myInt = 10;
long myLong = myInt; // 这里发生了从 int 到 long 的隐式类型转换
float myFloat = 3.14f;
double myDouble = myFloat; // 这里发生了从 float 到 double 的隐式类型转换
Console.WriteLine("整数转换为长整型:" + myLong);
Console.WriteLine("浮点数转换为双精度浮点型:" + myDouble);
}
}
在这个示例中,我们定义了一个整型变量 myInt 和一个长整型变量 myLong,以及一个浮点型变量 myFloat 和一个双精度浮点型变量 myDouble。在赋值操作中,我们没有显式地调用转换操作符,而是直接将 myInt 赋值给 myLong,将 myFloat 赋值给 myDouble,这样就触发了隐式类型转换。
当我们运行这段代码时,将会看到输出结果如下:
整数转换为长整型:10
浮点数转换为双精度浮点型:3.140000104904175
在C#中,显式类型转换是通过强制类型转换符号来实现的,它用于将一个数据类型转换为另一个数据类型,特别是当需要将一个较大范围的数据类型转换为一个较小范围的数据类型时,或者需要将一个对象类型转换为另一个对象类型时。
在进行显式类型转换时,我们要注意一些潜在的问题,其中包括数据丢失和溢出。因为将一个较大范围的数据类型转换为一个较小范围的数据类型时,可能会导致精度的丢失或者数据溢出。这需要我们在进行显式类型转换时格外小心,确保转换过程中不会丢失必要的信息或者导致不可预料的问题。
以下是一个显式类型转换的示例,演示了将一个双精度浮点数转换为整数的过程:
using System;
class Program
{
static void Main()
{
double myDouble = 3.14;
int myInt = (int)myDouble; // 这里进行了从 double 到 int 的显式类型转换
Console.WriteLine("双精度浮点型转换为整数:" + myInt);
}
}
当我们运行这段代码时,将会看到输出结果如下:
双精度浮点型转换为整数:3
在这个示例中,我们使用了 (int) 强制类型转换符号将双精度浮点数转换为整数。需要注意的是,这种显式转换可能造成小数部分的丢失,因此需要我们小心考虑何时以及如何使用显式类型转换。
C#提供了一系列内置的类型转换方法来进行不同数据类型之间的转换。这些方法能够帮助我们在需要时进行数据类型的转换,确保数据在不同类型之间能够正确地转换和处理。以下是这些内置的类型转换方法的简要描述:
通过使用这些内置的类型转换方法,我们可以在不同数据类型之间进行转换,并根据需要选择合适的转换方法,确保数据转换的准确性和可靠性。
下面是使用一些内置的类型转换方法的简单代码示例:
using System;
class Program
{
static void Main()
{
// 转换为布尔型
int intValue = 1;
bool boolValue = Convert.ToBoolean(intValue);
Console.WriteLine(boolValue); // 输出:True
// 转换为字符串类型
double doubleValue = 3.14;
string stringValue = Convert.ToString(doubleValue);
Console.WriteLine(stringValue); // 输出:"3.14"
// 转换为日期时间结构
string dateString = "2023-11-10";
DateTime dateValue = Convert.ToDateTime(dateString);
Console.WriteLine(dateValue); // 输出:2023/11/10 0:00:00
}
}
在这个简单的示例中,我们使用了Convert.ToBoolean、Convert.ToString和Convert.ToDateTime等方法来进行不同类型之间的转换。这些方法可以接受不同的输入类型,并将其转换为目标类型。
在进行类型转换时需要注意以下几点:
隐式转换只能将较小范围的数据类型转换为较大范围的数据类型,不能将较大范围的数据类型转换为较小范围的数据类型:这是因为较小范围的数据类型所能表示的值范围比较大范围的数据类型要小,因此从较小范围的类型转换到较大范围的类型时不会发生数据丢失。但是反过来,从较大范围的类型转换到较小范围的类型时可能会导致数据溢出或丢失,因此需要进行显式转换并进行适当的数据范围检查。
显式转换可能会导致数据丢失或精度降低,需要进行数据类型的兼容性检查:在进行显式转换时,我们需要注意目标数据类型是否能够容纳源数据类型的值,以避免数据溢出或丢失。例如,将一个双精度浮点数转换为单精度浮点数时,可能会丢失部分精度;将一个较大的整数类型转换为较小的整数类型时,可能会发生数据溢出。因此,在进行显式转换时需要进行兼容性检查,确保转换操作不会导致数据丢失或精度降低。
对于对象类型的转换,需要进行类型转换的兼容性检查和类型转换的安全性检查:在进行对象类型的转换时,需要首先进行兼容性检查,确保源类型和目标类型之间存在继承或接口实现关系,或者进行类型转换是否合法。另外,还需要进行类型转换的安全性检查,以确保转换操作不会导致类型不安全的情况发生,比如将一个不可空类型转换为可空类型时需要格外小心。
总之,对于任何类型转换操作,都需要仔细考虑数据范围、精度以及安全性等因素,确保转换操作能够正确、安全地进行。这样可以避免在程序运行过程中出现意外的错误或数据损失。