C# 语言引用类型
C# 中有两种类型:引用类型和值类型。引用类型的变量存储对其数据(对象)的引用,而值类型的变量直接包含其数据。对于引用类型,两种变量可引用同一对象。因此,对一个变量执行的操作会影响另一个变量所引用的对象。对于值类型,每个变量都具有其自己的数据副本,对一个变量执行的操作不会影响另一个变量(in、ref 和 out 参数变量除外)。
内置应用类型
C# 具有多个内置引用类型。 这些类型包含的关键字或运算符是 .NET 库中的类型的同义词。
对象类型
object 类型是System.Object 在 .NET 中的别名。 在 C# 的统一类型系统中,所有类型(预定义类型、用户定义类型、引用类型和值类型)都是直接或间接从 System.Object 继承的。可以将任何类型的值赋给 object 类型的变量。 可以使用文本 null 将任何 object 变量赋默认值。
& 将值类型的变量转换为对象的过程称为 装箱。将 object 类型的变量转换为值类型的过程称为 拆箱。
字符串类型
string 类型表示零个或多个 Unicode 字符的序列。string 是 System.String 在 .NET 中的别名。尽管 string 为引用类型,但是定义相等运算符 == 和 != 是为了比较 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 a = "good " + "morning";
上面的示例将创建包含“good morning”的字符串对象。
字符串是不可变的,即:字符串对象在创建后,尽管从语法上看似乎可以更改其内容,但事实上并不可行。 例如下面的例子:编译器实际上会创建一个新的字符串对象来保存新的字符序列,且该新对象将赋给 b 。 已为 b 分配的内存(当它包含字符串“h”时)可用于垃圾回收。
string b = "h";b += "ello";
[] 运算符可用于只读访问字符串的个别字符。有效索引于 0 开始,且必须小于字符串的长度:
string str = "test";char x = str[2]; // x = 's';
同样, [] 运算符也可用于循环访问字符串中的每个字符:
string str = "test";for (int i = 0; i < str.Length; i++){Console.Write(str[i] + " ");}// 输出: t e s t
字符串文本属于 string 类型且可以两种形式编写(带引号和仅 @ 带引号)。带引号字符串括在双引号 (") 内。
"good morning" // 字符串
字符串文本可包含任何字符文本。包括转义序列。下面的示例使用转义序列 \\ 表示反斜杠,使用 \u0066 表示字母 f,以及使用 \n表示换行符。
string a = "\\\u0066\n F";Console.WriteLine(a);// 输出:// \f// F
逐字字符串文本以 @ 开头,并且也括在双引号内。 例如:
@"good morning" // 字符串
逐字字符串的优点是不处理转义序列,这样就可轻松编写完全限定的 Windows 文件名等:
@"c:\Docs\Source\a.txt"
若要在 @-quoted 字符串中包含双引号,双倍添加即可:
@"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.
委托类型
委托类型的声明与方法签名相似。它有一个返回值和任意数目任意类型的参数:
public delegate void MessageDelegate(string message);public delegate int AnotherDelegate(MyType m, long num);
在 .NET 中, System.Action 和 System.Func 类型为许多常见委托提供泛型定义。可能不需要定义新的自定义委托类型。相反,可以创建提供的泛型类型的实例化。
delegate 是一种可用于封装命名方法或匿名方法的引用类型。委托类似于 C++中的函数指针;但是,委托是类型安全和可靠的。委托是事件的基础。通过将委托与命名方法或匿名方法关联,可以实例化委托。
必须使用具有兼容返回类型和输入参数的方法或 lambda 表达式实例化委托。为了与匿名方法一起使用,委托和与之关联的代码必须一起声明。
动态类型
dynamic 类型表示变量的使用和对其成员的引用绕过编译时类型检查,改为在运行时解析这些操作。dynamic类型简化了对 COM API(例如 Office Automation API)、动态 API(例如 IronPython 库)和 HTML 文档对象模型(DOM) 的访问。
在大多数情况下, dynamic 类型与 object 类型的行为类似。具体而言,任何非 Null 表达式都可以转换为dynamic 类型。 dynamic 类型与 object 的不同之处在于,编译器不会对包含类型 dynamic 的表达式的操作进行解析或类型检查。编译器将有关该操作信息打包在一起,之后这些信息会用于在运行时评估操作。在此过程中,dynamic 类型的变量会编译为 object 类型的变量。 因此,dynamic 类型只在编译时存在,在运行时则不存在。
下面的示例将 dynamic 类型的变量与 object 类型的变量进行对比。若要在编译时验证每个变量的类型,请将鼠标指针放在 WriteLine 语句中的 dyn 或 obj 上。 请将下面的代码复制到可以使用 IntelliSense 的编辑器中。IntelliSense 会对 dyn 显示“dynamic”,对 obj 显示“object”。
class Program{static void Main(string[] args){dynamic dyn = 1;object obj = 1;// Rest the mouse pointer over dyn and obj to see their// types at compile time.System.Console.WriteLine(dyn.GetType());System.Console.WriteLine(obj.GetType());}}
WriteLine 语句显示 dyn 和 obj 的运行时类型。此时,两者的类型均为整数。将生成以下输出:
System.Int32System.Int32
若要查看编译时 dyn 与 obj 之间的区别,请在前面示例的声明和 WriteLine 语句之间添加下列两行:
dyn = dyn + 3;obj = obj + 3;
尝试在表达式 obj + 3 中添加整数和对象时,将报告编译器错误。但是,对于 dyn + 3 ,不会报告任何错误。 在编译时不会检查包含 dyn 的表达式,原因是 dyn 的类型为 dynamic 。
下面的示例在多个声明中使用 dynamic 。 Main 方法也将编译时类型检查与运行时类型检查进行了对比。
using System;namespace DynamicExamples{class Program{static void Main(string[] args){ExampleClass ec = new ExampleClass();Console.WriteLine(ec.exampleMethod(10));Console.WriteLine(ec.exampleMethod("value"));// The following line causes a compiler error because exampleMethod// takes only one argument.//Console.WriteLine(ec.exampleMethod(10, 4));dynamic dynamic_ec = new ExampleClass();Console.WriteLine(dynamic_ec.exampleMethod(10));// Because dynamic_ec is dynamic, the following call to exampleMethod// with two arguments does not produce an error at compile time.// However, it does cause a run-time error.//Console.WriteLine(dynamic_ec.exampleMethod(10, 4));}}class ExampleClass{static dynamic field;dynamic prop { get; set; }public dynamic exampleMethod(dynamic d){dynamic local = "Local variable";int two = 2;if (d is int){return local;}else{return two;}}}}// Results:// Local variable// 2// Local variable
本篇文章简单的讲述了引用类型的定义与一些常用的内置引用类型,后续章节会对这些内置引用类型做比较详细的说明。