object 类型
object 类型是 System . Object 在 .NET 中的别名。 在 C# 的统一类型系统中,所有类型(预定义类型、用户定义类型、引用类型和值类型)都是直接或间接从 System . Object 继承的。 可以将任何类型的值(除 ref struct 外)分配给类型的 object 变量。 可以使用文本 null 将任何 object 变量赋值给其默认值。将值类型的变量转换为对象的过程称为装箱(boxed)。 将 object 类型的变量转换为值类型的过程称为取消装箱(拆箱,unboxed)。
字符串类型
string 类型表示零个或多个 Unicode 字符的序列。 string 是 System . String 在 .NET 中的别名。
尽管 string 为引用类型,但是定义相等运算符 “==” 和 “!=” 是为了比较 string 对象(而不是引用)的值。 基于值的相等性使得对字符串相等性的测试更为直观。 例如:
string a = "hello";
string b = "h";
// 向 “b” 添加内容
b += "ello";
Console . WriteLine ( a == b ); // 返回 True
Console . WriteLine ( object . ReferenceEquals ( a , b ) ); // 返回 False
“==” 返回 True 是因为两个 string 确实是一个值(都是 “hello”);object . ReferenceEquals 返回 False 是因为两个 string 确实没有引用同一个 string 实例。
“+” 操作符连接字符串:string a = "value " + "type"
前面的代码创建了一个包含 “value type” 的字符串对象。
string 是不可变的 - 在对象创建之后,string 对象的内容不能被改变。例如,当您编写这段代码时,编译器实际上创建了一个新的 string 对象来保存新的字符序列,并将该新对象分配给 zfcb。已经为 zfcb 分配的内存(当它包含字符串 “h” 时)然后有资格进行垃圾收集。
string zfcb = "h";
zfcb += "ello";
“[ ]”操作符可用于对字符串中的单个字符进行只读访问。有效的索引值从 0 开始,并且必须小于字符串的长度:
string zfc = "我的家庭";
char zf = zfc [ 2 ]; // x = '家';
类似地,“[ ]” 操作符也可用于遍历字符串中的每个字符:
string zfc = "我的家庭";
for ( int i = 0 ; i < zfc . Length ; i++)
{
Console . Write ( zfc [ i ] + " ");
}
// Output: 我 的 家 庭
string 字面值
字符串字面值是字符串类型,可以写成三种形式:原始的(raw)、引用的(quoted)和逐字的(verbatim)。
原始字符串字面量从 C# 11 开始可用。原始字符串字面量可以包含任意文本,而不需要转义序列。原始字符串文字可以包括空格和新行、嵌入引号和其他特殊字符。原始字符串文字包含在至少三个双引号(""")中:
"""
这是多行
第二行缩进的字符串字面值。
"""
您甚至可以包含三个(或更多)双引号字符的序列。如果你的文本需要一个嵌入的引号序列,你可以根据需要用更多的引号开始和结束原始字符串文字:
"""""
这段原始 string 字面值有四个 """",数一数:"""" 四个!
在序列中嵌入引号字符。这就是为什么开头和结尾都有五个双引号。
您可以扩展此示例,根据文本需要使用任意多的嵌入引号。
"""""
原始 string 字面值通常在与嵌入文本分开的行上具有起始和结束引号序列。多行原始字符串字面量支持本身就是引号字符串的字符串:
string zfc =
"""
"这是非常重要的信息。"
""";
Console . WriteLine ( zfc ); // 输出 "这是非常重要的信息。"最左边的空格不是原始字符串字面值的一部分
当开始引号和结束引号位于不同的行时,开始引号之后和结束引号之前的换行符不包括在最终内容中。结束引号序列规定了 string 字面值的最左边的列。您可以缩进原始 string 文字以匹配整体代码格式。
保留结束引号序列右侧的列。该行为为JSON、YAML 或 XML 等数据格式启用原始字符串,如下例所示:
var json =
"""
{
"属性":0
}
""";
Console . WriteLine ( json );
上例输出:
{
"属性":0
}
如果任何文本行延伸到结束引号序列的左侧,编译器将发出错误。起始和结束引号序列可以在同一行,前提是 string 字面值既不以引号字符开始也不以引号字符结束:
var zfc短文本 = """在早上,你见了老师要说 "Good morning!"。""";
输出:
在早上,你见了老师要说 "Good morning!"。
您可以将原始 string 文字与 string 插值相结合,以在输出字符串中包含引号字符和大括号。
带引号的 string 字面值用双引号(")括起来:"上帝保佑" // string 字面值
string 字面值可以包含任何字符字面值。转义序列包括在内。下面的示例使用转义序列“\”表示反斜杠,“\u0066”表示字母 f, “\n”表示 newline。
string a = "\\\u0066\n F";
Console . WriteLine ( a );
// 输出:
// \f
// F
请注意:转义码“\dddd”(其中 dddd 是一个四位数)表示 Unicode 字符 U + dddd。也可以识别 8 位 Unicode 转义码:“\Udddddddd”。
逐字字符串以“@”开头,也用双引号括起来。例如:@"good morning" // string 字面值
逐字字符串的优点是不处理转义序列,这使得它易于编写。例如,下面的文本匹配一个完全限定的 Windows 文件名:
@“C:\MyGames\List.html” // 等效于 “C:\\MyGames\\List.html”
要在“@”引号字符串中包含双引号,请使用两个双引号:@"""啊!"" 小红终于想起答案了。" // "啊!" 小红终于想起答案了。
UTF-8 string 字面值
.NET 中的 string 使用 UTF-16 编码存储。UTF-8 是 Web 协议和其他重要库的标准。从 C# 11 开始,您可以向 string 字面值添加 u8 后缀以指定 UTF-8 编码。UTF-8 字面值存储为 ReadOnlySpan < byte > 对象。UTF-8 string 字面值的自然类型是 ReadOnlySpan < byte >。使用 UTF-8 string 文字比声明创建等效的 System . ReadOnlySpan < T > 更清晰的声明,如下代码所示:
ReadOnlySpan < byte > AuthWithTrailingSpace = new byte [ ] { 0x41, 0x55, 0x54, 0x48, 0x20 };
ReadOnlySpan < byte > AuthStringLiteral = "AUTH "u8;
上述两者等效,C# 会提示你下者更清晰。
将 UTF-8 string 字面值存储为数组需要使用 ReadOnlySpan < T > . ToArray ( ) 将包含该字面值的字节复制到可变数组中。
UTF-8 string 字面值不是编译时间常量;它们是运行时常数。因此,它们不能用作可选参数的默认值。UTF-8 string 字面值不能与 string 插值结合使用。不能在同一个 string 表达式上使用“$”标记和“u8”后缀。
delegate 委托类型
委托类型的声明类似于方法签名。它有一个返回值和任意数量的任意类型的形参:
public delegate void WT信息 ( string 信息 );
public delegate int WT( LX我的 m , long 数 );
在 .NET 中,System . Action 和 System . Func 类型为许多常见委托提供了泛型定义。您可能不需要定义新的自定义委托类型。相反,您可以创建所提供泛型类型的实例化。
委托是一种引用类型,可用于封装命名方法或匿名方法。委托类似于 C++ 中的函数指针;然而,委托是类型安全且可靠的。委托是事件的基础。可以通过将委托与命名方法或匿名方法关联来实例化委托。
必须使用具有兼容的返回类型和输入参数的方法或 lambda 表达式实例化委托。为了与匿名方法一起使用,委托和与之关联的代码被一起声明。
当运行时涉及的委托类型由于变量转换而不同时,委托组合或删除失败并出现运行时异常。下面的例子演示了一个失败的情况:
Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};
// 由于 objectAction 到 Action < string > 的隐式引用转换而有效,但可能在运行时失败。
Action<string> combination = stringAction + objectAction;
可以通过创建新的 delegate 对象来创建具有正确运行时类型的委托。下面的示例演示如何将此解决方案应用于前面的示例。
Action < string > stringAction = str => { };
Action < object > objectAction = obj => { };
// 创建一个运行时类型为 Action < string > 的新委托实例
Action < string > wrappedObjectAction = new Action < string > ( objectAction );
// 两个 Action < string > 委托实例现在可以合并了
Action < string > combination = stringAction + wrappedObjectAction;
您可以声明函数指针,它们使用类似的语法。函数指针使用 calli 指令,而不是实例化委托类型并调用虚拟 Invoke 方法。
dynamic 动态类型
动态类型指示变量的使用和对其成员的引用绕过编译时类型检查。相反,这些操作在运行时解决。动态类型简化了对 COM api(如 Office Automation api)、对动态 api(如 IronPython 库)和 HTML 文档对象模型(DOM)的访问。
在大多数情况下,动态类型的行为类似于对象类型。特别是,任何非空表达式都可以转换为动态类型。动态类型与对象的不同之处在于,包含动态类型表达式的操作不会被编译器解析或检查类型。编译器将有关操作的信息打包在一起,这些信息稍后用于在运行时计算操作。作为这个过程的一部分,动态类型的变量被编译成对象类型的变量。因此,动态类型只存在于编译时,而不存在于运行时。
下面的示例将动态类型的变量与对象类型的变量进行对比。要在编译时验证每个变量的类型,请将鼠标指针放在 WriteLine 语句中的 DT 或 DX 上。将以下代码复制到 IntelliSense 可用的编辑器中。智能感知对 DT 显示动态,对 DX 显示对象。
dynamic DT = 1;
object DX = 1;
// 将鼠标指针停留在 DT 和 DX 上,在编译时查看它们的类型
Console . WriteLine ( DT . GetType ( ) );
Console . WriteLine ( DX . GetType ( ) );
WriteLine 语句显示 DT 和 DX 的运行时类型。在这一点上,两者具有相同的类型,整数。输出结果如下:
System.Int32
System.Int32
要查看 DT 和 DX 在编译时的区别,请在前面示例中的声明和 WriteLine 语句之间添加以下两行。
DT += 3;
DX += 3;
在表达式 DX += 3 中尝试添加一个整数和一个对象会报告编译器错误。然而,DT += 3 没有错误报告。包含 DT 的表达式在编译时不会被检查,因为 DT 的类型是动态的。
下面的例子在几个声明中使用了 dynamic。Main 方法还将编译时类型检查与运行时类型检查进行对比。
{
class Program
{
static void Main ( string [ ] args )
{
ExampleClass ec = new ExampleClass ( );
Console . WriteLine ( ec . ExampleMethod ( 10 ) );
Console . WriteLine ( ec . ExampleMethod ( "值" ) );
// 下面一行会导致编译器错误,因为 ExampleMethod 只接受一个参数
// Console . WriteLine ( ec . ExampleMethod ( 10 , 4 ) );
dynamic dynamic_ec = new ExampleClass ( );
Console . WriteLine ( dynamic_ec . ExampleMethod ( 10 ) );
// 因为 dynamic_ec 是动态的,所以下面用两个参数调用 ExampleMethod 不会在编译时产生错误
// 但是,它确实会导致运行时错误
// Console . WriteLine ( dynamic_ec . ExampleMethod ( 10 , 4 ) );
}
}
class ExampleClass
{
static dynamic _field;
dynamic Prop { get; set; }
public dynamic ExampleMethod ( dynamic d )
{
dynamic local = "局部变量";
int two = 2;
if ( d is int )
{
return local;
}
else
{
return two;
}
}
}
}
// 输出:
// 局部变量
// 2
// 局部变量
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。