C#介绍&数据类型
介绍
源于C,面向对象,面向组件
运行于.Net
.NET 是名为公共语言运行时 (CLR) 的虚执行系统和一组类库。 CLR 是 Microsoft 对公共语言基础结构 (CLI) 国际标准的实现。 CLI 是创建执行和开发环境的基础
语言互操作性
通过 C# 生成的 IL 代码符合公共类型规范 (CTS),可以与通过 .NET 版本的 F#、Visual Basic、C++ 等生成的代码进行交互。单个程序集可包含多个用不同 .NET 语言编写的模块。 这些类型可以相互引用,就像它们是用同一种语言编写的一样。
Hello World
.Net的库被整理到命名空间中
using System;
//C#9以后
//Console.WriteLine("Hello, World");
class Hello
{
static void Main()
{
Console.WriteLine("Hello, World");
}
}
Main 静态方法是 C# 程序的入口点。C#9以后,可以在顶层书写代码作为程序入口。
命名空间用于组织 C# 程序和库的分层。包含类和其他命名空间。例如,程序中引用的 System 命名空间Console 类。和其他许多命名空间如 IO 。
由于使用 using 指令,因此程序可以使用 Console.WriteLine 作为 System.Console.WriteLine 的简写。默认情况下,编译器会自动引用标准类库。
数据类型
值类型和引用类型是 C# 类型的两个主要类别。 值类型的变量包含类型的实例。 后者包含对类型实例的引用。
每个 C# 类型关键字都是相应 .NET 类型的别名。 关键字和 .NET 类型名称是可互换的。如
int a = 123;
System.Int32 b = 123;
//可用 default 运算符生成默认类型值
a = default;
值类型
对于值类型,每个变量都具有其自己的数据副本,对一个变量执行的操作不会影响另一个变量
(in、ref 和 out 参数变量除外)。
简单类型
所有数字可用下划线进行分隔,如3.934_001
整数类型
- 有符号整型:sbyte、short、int、long
- 无符号整型:byte、ushort、uint、ulong
每个整型类型的默认值都为零 0。
每个整型类型都有 MinValue 和 MaxValue 属性,提供该类型的最小值和最大值。
System.Numerics.BigInteger 结构用于表示没有上限或下限的带符号整数。
由小变大会隐式转换
浮点数类型
- float、double
- 高精度十进制浮点数:decimal
每个浮点类型的默认值都为0。
只有float变double一种隐式转换
每个浮点类型都有 MinValue 和 MaxValue 常量,提供该类型的最小值和最大有限值。 float和double 类型还提供可表示非数字和无穷大值的常量。 例如,double 类型提供以下常量:Double.NaN、Double.NegativeInfinity 和 Double.PositiveInfinity。
decimal避免意外舍入错误
- 不带后缀的文本或带有 d 或 D 后缀的文本的类型为 double
- 带有 f 或 F 后缀的文本的类型为 float
- 带有 m 或 M 后缀的文本的类型为 decimal
Bool
默认值为false
Char
表示 Unicode UTF-16 字符。
默认值为 \0,即 U+0000。
char 类型可隐式转换为以下整型类型:ushort、int、uint、long 和 ulong。 它也可以隐式转换为内置浮点数值类型:float、double 和 decimal。 它可以显式转换为 sbyte、byte 和 short 整型类型。
无法将其他类型隐式转换为 char 类型。 但是,任何整型或浮点数值类型都可显式转换为 char。
枚举类型
由基础整型数值类型的一组命名常量定义,如:
enum Season
{
Spring,
Summer,
Autumn,
Winter
}
public class EnumConversionExample
{
public static void Main()
{
Season a = Season.Autumn;
Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2
var b = (Season)1;
Console.WriteLine(b); // output: Summer
var c = (Season)4;
Console.WriteLine(c); // output: 4
}
}
默认情况下,枚举成员的关联常数值为类型 int;它们从零开始,并按定义文本顺序递增 1。也可以显式指定,如:
enum ErrorCode : ushort
{
None = 0,
Unknown = 1,
ConnectionLost = 100,
OutlierReading = 200
}
结构类型
一种可封装数据和相关功能的值类型 。
public struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; }
public double Y { get; }
public override string ToString() => $"({X}, {Y})";
}
通常,可以使用结构类型来设计以数据为中心的较小类型,这些类型只有很少的行为或没有行为。
如果侧重于类型的行为,请考虑定义一个类。
readonly 结构
由于结构类型具有值语义,因此建议定义不可变的结构类型。
可以使用 readonly 修饰符来声明结构类型为不可变。 readonly 结构的所有数据成员都必须是只读的,保证 readonly 结构的成员不会修改该结构的状态。 这意味着除构造函数外的其他实例成员是隐式 readonly。
元组类型
将多个数据元素分组成一个轻型数据结构。不能在元组类型中定义方法
(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.
(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.
var t3 =
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26);
Console.WriteLine(t3.Item26); // output: 26
最常见的用法是作为方法返回类型。
可以在元组初始化表达式中或元组类型的定义中显式指定元组字段名称
var t = (Sum: 4.5, Count: 3);
Console.WriteLine($"Sum of {t.Count} elements is {t.Sum}.");
//元组投影初始值设定项
var sum = 4.5;
var count = 3;
var t = (sum, count);
Console.WriteLine($"Sum of {t.count} elements is {t.sum}.");
以下情况下,变量名称不会被投影到元组字段名称中:
- 候选名称是元组类型的成员名称,例如 Item3或ToString 。
- 候选名称是另一元组的显式或隐式字段名称的重复项。
元组字段的默认名称为 Item1、Item2、Item3 等。 始终可以使用字段的默认名称,即使字段名称是显式指定的或推断出
在编译时,编译器会将非默认字段名称替换为相应的默认名称。 因此,显式指定或推断的字段名称在运行时不可用。
C# 支持满足以下两个条件的元组类型之间的赋值:
- 两个元组类型有相同数量的元素
- 对于每个元组位置,右侧元组元素的类型与左侧相应的元组元素的类型相同或可以隐式转换为左侧相应的元组元素的类型
(int, double) t1 = (17, 3.14);
(double First, double Second) t2 = (0.0, 1.0);
t2 = t1;
Console.WriteLine($"{nameof(t2)}: {t2.First} and {t2.Second}");
// Output: t2: 17 and 3.14
//在括号外使用 var 关键字来声明隐式类型化变量,并让编译器推断其类型
var t = ("post office", 3.6);
var (destination, distance) = t;
Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
// Output:Distance to post office is 3.6 kilometers.
//在括号内显式声明每个变量的类型:
var t = ("post office", 3.6);
(string destination, double distance) = t;
Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
// Output:Distance to post office is 3.6 kilometers.
var t = ("post office", 3.6);
(var destination, double distance) = t;
Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
// Output:Distance to post office is 3.6 kilometers.
//使用现有变量
var destination = string.Empty;
var distance = 0.0;
var t = ("post office", 3.6);
(destination, distance) = t;
Console.WriteLine($"Distance to {destination} is {distance} kilometers.");
// Output:Distance to post office is 3.6 kilometers.
元组== 和 != 操作不会考虑元组字段名称。
(int a, byte b) left = (5, 10);
(long a, int b) right = (5, 10);
Console.WriteLine(left == right); // output: True
Console.WriteLine(left != right); // output: False
var t1 = (A: 5, B: 10);
var t2 = (B: 5, A: 10);
Console.WriteLine(t1 == t2); // output: True
Console.WriteLine(t1 != t2); // output: False
System.ValueTuple 类型与 System.Tuple 类型表示的元组。 主要区别如下:
- System.ValueTuple 类型是值类型。 System.Tuple 类型是引用类型。
- System.ValueTuple 类型是可变的。 System.Tuple 类型是不可变的。
- System.ValueTuple 类型的数据成员是字段。 System.Tuple 类型的数据成员是属性。
可为空的值类型
T? 表示其基础值类型T 的所有值及额外的 null 值。 例如, bool? 可指定的值包括:true、false 或 null。
用于在某些应用程序中,变量值可能未定义或缺失时。
可为空值类型的默认值表示 null
任何可为空的值类型都是泛型 System.Nullable 结构的实例。所以Nullable 或 T?都可以引用具有基础类型 T 的可为空值类型。
三种方式,既检查 null 的可为空值类型的实例,又检索基础类型的值:
int? a = 42;
if (a is int valueOfA)
{
Console.WriteLine($"a is {valueOfA}");
}
else
{
Console.WriteLine("a does not have a value");
}
// Output:a is 42
int? b = 10;
if (b.HasValue)
{
Console.WriteLine($"b is {b.Value}");
}
else
{
Console.WriteLine("b does not have a value");
}
// Output:b is 10
int? c = 7;
if (c != null)
{
Console.WriteLine($"c is {c.Value}");
}
else
{
Console.WriteLine("c does not have a value");
}
// Output:c is 7
可为空的值类型转换为基础类型
int? a = 28;
int b = a ?? -1;
Console.WriteLine($"b is {b}"); // output: b is 28
int? c = null;
int d = c ?? -1;
Console.WriteLine($"d is {d}"); // output: d is -1
int? n = null;
//int m1 = n; // Doesn't compile
int n2 = (int)n; // Compiles, but throws an exception if n is null
//如果要使用基础值类型的默认值来替代 null
n.GetValueOrDefault()
如果一个操作数为null则运算结果为null
对于比较运算符<、>、<= 和 >=,如果一个或全部两个操作数都为 null,则结果为 false
对于相等运算符==,如果两个操作数都为 null,则结果为 true;如果只有一个操作数为 null,则结果为 false;
对于不等运算符!=,如果两个操作数都为 null,则结果为 false;如果只有一个操作数为 null,则结果为 true
引用类型
对于引用类型,两种变量可引用同一对象;因此,对一个变量执行的操作会影响另一个变量所引用的对象。
内置引用类型
对象类型
所有类型都是直接或间接从 System.Object 继承的。
可以将任何类型的值赋给 object 类型的变量。
可以使用文本 null 将任何 object 变量赋值给其默认值。
将值类型的变量转换为对象的过程称为装箱。
int i = 123; // a value type
object o = i; // boxing
int j = (int)o; // unboxing
装箱可以保证数据一致,并且在垃圾回收堆中存储值类型。
字符串类型
== 和 != 是为了比较 string 对象(而不是引用)的值。
string a = "hello";
string b = "h";
// Append to contents of 'b'
b += "ello";
Console.WriteLine(a == b);
Console.WriteLine(object.ReferenceEquals(a, b));
//前面显示“True”,然后显示“False”,因为字符串的内容是相等的,但 a 和 b 并不指代同一字符串实例。
字符串是不可变的。
string b = "h";
b += "ello";
//编写此代码时,编译器实际上会创建一个新的字符串对象来保存新的字符序列
//且该新对象将赋给 b。 已为 b 分配的内存(当它包含字符串“h”时)可用于垃圾回收。
原始字符串字面量从 C# 11 开始可用。 字符串字面量可以包含任意文本,而无需转义序列。
原始字符串字面量用至少三个双引号 (“”") 括起来:
"""
This is a multi-line
string literal with the second line indented.
"""
//如果字符串中出现三个双引号,则增加头尾双引号数量
var message = """
"This is a very important message."
""";
Console.WriteLine(message);
// output: "This is a very important message."
//左引号和右引号序列可以位于同一行上,前提是字符串字面量既不能以引号字符开头,也不能以引号字符结尾
var shortText = """He said "hello!" this morning.""";
保留结束引号序列右侧的列。 此行为将为 JSON、YAML 或 XML 等数据格式启用原始字符串
var json= """
{
"prop": 0
}
""";
逐字字符串文本以 @ 开头,并且也括在双引号内。
逐字字符串的优点是不处理转义序列,这样就可轻松编写。
@"c:\Docs\Source\a.txt"
// rather than "c:\\Docs\\Source\\a.txt"
//若要在用 @ 引起来的字符串中包含双引号,双倍添加即可:
@"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.
UTF-8字面量
.NET 中的字符串是使用 UTF-16 编码存储的。 UTF-8 是 Web 协议和其他重要库的标准。 从 C# 11 开始,可以将 u8 后缀添加到字符串字面量以指定 UTF-8 编码。
UTF-8 字面量存储为 ReadOnlySpan 对象。 UTF-8 字符串字面量的自然类型是 ReadOnlySpan。
ReadOnlySpan<byte> AuthWithTrailingSpace = new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
ReadOnlySpan<byte> AuthStringLiteral = "AUTH "u8;
// UTF-8 字符串字面量存储为数组
byte[] AuthStringLiteral = "AUTH "u8.ToArray();
委托类型
之后补全
动态类型
dynamic 类型表示变量的使用和对其成员的引用绕过编译时类型检查。 改为在运行时解析这些操作。
在大多数情况下,dynamic 类型与 object 类型的行为类似。 具体而言,任何非 Null 表达式都可以转换为 dynamic 类型。
dynamic 类型与 object 的不同之处在于,编译器不会对包含类型 dynamic 的表达式的操作进行解析或类型检查。