C# 是一种强类型语言。 每个变量、常量、求值的表达式都有一个类型。 每个方法声明都为每个输入参数和返回值指定名称、参数数量以及类型和种类(值、引用或输出)。
float temperature;
string name;
MyClass myClass;
char firstLetter = 'C';
var limit = 3;
int[] source = { 0, 1, 2, 3, 4, 5 };
var query = from item in source
where item <= limit
select item;
C#数据类型
类型名称 .NET类型 类型分类Ⅰ 类型分类Ⅱ bool System.Boolean 值类型 内置类型 byte System.Byte 值类型 内置类型 sbyte System.SByte 值类型 内置类型 char System.Char 值类型 内置类型 decimal System.Decimal 值类型 内置类型 double System.Double 值类型 内置类型 float System.Single 值类型 内置类型 int System.Int32 值类型 内置类型 uint System.UInt32 值类型 内置类型 nint System.IntPtr 值类型 内置类型 nuint System.UIntPtr 值类型 内置类型 long System.Int64 值类型 内置类型 ulong System.UInt64 值类型 内置类型 short System.Int16 值类型 内置类型 struct System.ValueType 值类型 可自定义类型 enum System.ValueType 值类型 可自定义类型 string System.String 引用类型 内置类型 class System.Object 引用类型 可自定义类型 record System.Object 引用类型 可自定义类型 interface System.Object 引用类型 可自定义类型 delegate System.Object 引用类型 内置类型
## 值类型
值类型变量直接包含它们的值,这意味着在声明变量的任何上下文中内联分配内存。 对于值类型变量,没有单独的堆分配或垃圾回收开销。
使用 struct 关键字可以创建你自己的自定义值类型。 结构通常用作一小组相关变量的容器,如以下示例所示:
public struct Coords
{
public int x, y;
public Coords(int p1, int p2)
{
x = p1;
y = p2;
}
}
枚举定义的是一组已命名的整型常量。 例如,.NET 类库中的 System.IO.FileMode 枚举包含一组已命名的常量整数,用于指定打开文件应采用的方式。 下面的示例展示了具体定义:
public enum FileMode
{
CreateNew = 1,
Create = 2,
Open = 3,
OpenOrCreate = 4,
Truncate = 5,
Append = 6,
}
## 引用类型
定义为 class、record、delegate、数组或 interface 的类型是引用类型。 在运行时,当声明引用类型的变量时,该变量会一直包含值 null,直至使用 new 运算符显式创建对象,或者为该变量分配已经在其他位置使用new 创建的对象,如下所示:
MyClass mc = new MyClass();
MyClass mc2 = mc;
由于 C# 是在编译时静态类型化的,因此变量在声明后就无法再次声明,或无法分配另一种类型的值,除非该类型可以隐式转换为变量的类型。 例如,string 无法隐式转换为 int。 因此,在将 i 声明为 int 后,无法将字符串“Hello”分配给它。 但有时可能需要将值复制到其他类型的变量或方法参数中。 例如,可能需要将一个整数变量传递给参数类型化为 double 的方法。 或者可能需要将类变量分配给接口类型的变量。 这些类型的操作称为类型转换。
对于内置数值类型,如果要存储的值无需截断或四舍五入即可适应变量,则可以进行隐式转换。 对于整型类型,这意味着源类型的范围是目标类型范围的正确子集。 例如,long 类型的变量(64 位整数)能够存储 int(32 位整数)可存储的任何值。
int num = 2147483647;
long bigNum = num;
对于引用类型,隐式转换始终存在于从一个类转换为该类的任何一个直接或间接的基类或接口的情况。 由于派生类始终包含基类的所有成员,因此不必使用任何特殊语法。
Derived d = new Derived();
Base b = d;
如果进行转换可能会导致信息丢失,则编译器会要求执行显式转换,显式转换也称为强制转换。 强制转换是显式告知编译器以下信息的一种方式:你打算进行转换且你知道可能会发生数据丢失,或者你知道强制转换有可能在运行时失败。 若要执行强制转换,请在要转换的值或变量前面的括号中指定要强制转换到的类型。 下面的程序将 double 强制转换为 int。如不强制转换则该程序不会进行编译。
class Test
{
static void Main()
{
double x = 1234.7;
int a;
// Cast double to int.
a = (int)x;
System.Console.WriteLine(a);
}
}
// Output: 1234
对于引用类型,如果需要从基类型转换为派生类型,则必须进行显式强制转换。
// Create a new derived type.
Giraffe g = new Giraffe();
// Implicit conversion to base type is safe.
Animal a = g;
// Explicit conversion is required to cast back
// to derived type. Note: This will compile but will
// throw an exception at run time if the right-side
// object is not in fact a Giraffe.
Giraffe g2 = (Giraffe)a;
若要在非兼容类型(如整数和 System.DateTime 对象,或十六进制字符串和字节数组)之间转换,可使用 System.BitConverter 类、System.Convert 类和内置数值类型的 Parse 方法(如 Int32.Parse)。
可使用一个或多个类型参数声明、作为客户端代码在创建类型实例时将提供的实际类型(具体类型)的占位符的类型。 这种类型称为泛型类型。 例如,.NET 类型 System.Collections.Generic.List<T> 具有一个类型参数,它按照惯例被命名为 T。 当创建类型的实例时,指定列表将包含的对象的类型,例如 string:
List<string> stringList = new List<string>();
stringList.Add("String example");
可以使用 var 关键字隐式键入一个局部变量(但不是类成员)。 var 指示编译器通过初始化语句右侧的表达式推断变量的类型。 推断类型可以是内置类型、匿名类型、用户定义类型或 .NET 类库中定义的类型。
var i = 5;
var s = "Hello";
var a = new[] { 0, 1, 2 };
var expr =
from c in customers
where c.City == "London"
select c;
var anon = new { Name = "Terry", Age = 34 };
var list = new List<int>();