在.NET Framework中大部分的类型是引用类型。引用类型提供了很大的灵活性,并且它们提供了完美的性能去把它们传到方法中。以下部分通过讨论公共内建的类介绍了引用类型。在第四课“类型的转化”中将会包含创建class,interface,和delegate.
学完这课后,你将能够:
■ 解释值类型与引用类型的不同。
■描述当赋值时,值类型与引用类型有什么不同。
■列出内建的引用类型。
■描述你何时使用StringBuilder类型。
■创建数组和数组内的排序。
■打开,读,写,和关闭文件。
■监测什么时候异常会发生并且对异常做如何反映。
什么是引用类型?
引用类型存储着引用类型的数据的地址,也被称为指针,并且引用类型在stack中。这个地址所指向的 实际数据被存储在一个被称为heap的内存区域中。运行时通过一个叫做garbage collection去管理被heap使用的内存。Garbage collection阶段性通过抛弃不再被引用的项去回收内存为以后使用。
Garbage collection
Garbage collection 仅仅在需要的时候,和触发了GC.Collect 的调用时发生。自动垃圾回收机制使应用中的大部分实例是短命的,除非它们是应用已开始被分配的。这样的设计模式导致了最好的性能。
引用和值类型的行为比较:
因为引用代表的是数据的地址而不是数据本身,把一个引用类型赋给另一个引用类型并不是拷贝数据,而仅仅是创建引用的一个副本,副本和初始变量引用堆中的相同的内存地址。
考虑以下简单的struct声明:
// C#
struct Numbers
{
public int val;
public Numbers(int _val)
{ val = _val; }
public override string ToString()
{ return val.ToString(); }
}
现在考虑以下代码,创建了一个Numbers的结构,并把一个副本拷给了第二个实例,修改两个值。
// C#
Numbers n1 = new Numbers(0);
Numbers n2 = n1;
n1.val += 1;
n2.val += 2;
Console.WriteLine("n1 = {0}, n2 = {1}", n1, n2);
代码将显示“n1=1,n2=2”因为结构是一个值类型,并且拷贝一个值类型导致两个不同的值。待如果把Numbers变为class,这个代码结果就为“n1=3,n2=3”。把Numbers从结构转变为类使它成为一个引用类型。当你修改一个引用类型时,你修改的是引用类型的所以副本。
内建引用类型
在.NET Framework中有大约2500个内建引用类型。凡是不继承System.ValueType的都是引用类型。表1-3中列出大部分的公共用途的类型,被许多其它的引用类型所继承。
Table 1-3 公共引用类型
Type Use for
System.Object Object 类型是Framework中最普遍的类型,你能把任何类型转化为 S System.Object.并且你能使用任何类型的继承Object类的
ToString, GetType, and Equals 成员。
System.String 文本数据。
System.Text.StringBuilder 动态文本数据。
System.Array 数据数组. 这是所有数组的基类。
System.IO.Stream 文件设备和I/O网络的缓冲。这是一个抽象类;特定的类
都继承Stream
System.Exception 处理系统和应用定义的异常。特定的异常继承此类型。
Strings and String Builders
这两个类型不仅仅是数据的容器,它们也通过它们的成员去操作数据。
System.String 提供一系列成员去操作文本。例如以下代码快速的搜索和替代。
// C#
string s = "this is some text to search";
s = s.Replace("search", "replace");
Console.WriteLine(s);
System.String类型中的字符串在.NET中是具有恒定性的。这意味着对字符串的任何改变都会引起运行时去创建一个新的字符串并放弃旧的字符串。许多程序员很奇怪的发现如下代码在内存中分配了四个新的字符串:
string s;
s = "wombat"; // "wombat"
s += " kangaroo"; // "wombat kangaroo"
s += " wallaby"; // "wombat kangaroo wallaby"
s += " koala"; // "wombat kangaroo wallaby koala"
Console.WriteLine(s);
仅仅最后的字符串有一个引用;其它三个将被垃圾回收销毁。避免这些临时字符串能够避免不必的垃圾回收,这样可以提升性能。有 三种方法避免临时字符串:
■使用String类中的Concat,Join, or Format 方法在一个单独的语句去连接多个项。
■ 使用StringBuilder class 去动态地创造易变的字符串.
默认的构造器创建一个16 bytes 长的缓冲,并随着需求而增长。
你能定义一个初始大小和最大值。如下所示:
System.Text.StringBuilder sb = new System.Text.StringBuilder(30);
sb.Append("wombat"); // Build string.
sb.Append(" kangaroo");
sb.Append(" wallaby");
sb.Append(" koala");
string s = sb.ToString(); // Copy result to string.
Console.WriteLine(s);
String class另一个精巧且重要的是它重载了System.Object.的操作符。
表1-4列出了String class重载的操作符:
Table 1-4 String 操作符
Operator C# Action on System.String
Addition + 连接两个字符串创建一个新的字符串。
Equality == 如果两个字符串有相同的内容返回True
如果不同返回False
Inequality != 相等操作符的相反操作.
Assignment = 把一个字符串拷贝到另一个。它引发字符串的行为像值类型一样。 即使它们是作为引用类型来实现的。
如何创建数组以及数组的排序
使用方括号来声明数组。对于String类型,System.Array提供了方法对它包含的数据进行操作。以下的代码用一些初始数据声明了一个数组,然后对这个数组排序。
// C#
// Declare and initialize an array.
int[] ar = { 3, 1, 2 };
// Call a shared/static array method.
Array.Sort(ar);
// Display the result.
Console.WriteLine("{0}, {1}, {2}", ar[0], ar[1], ar[2]);
如何使用流
流是另一个非常重要的通用类型,因为流是为了从硬盘中读和写和跨网络通信。System.IO.Stream类型是所有特定的流类型的基类。表 1-5显示了一些最普遍使用的流类型。同时,网络流System.Network.Sockets命名空间中,加密流在System.Security.Cryptography空间里。
最简单的流类是StreamReader 和 StreamWriter,它们能使你对文本文件读和写。你能传递一个文件名作为构造器的一部分,用一行代码就能打开一个文件。在你处理一个文件后,调用Close方法以至于文件不会被锁。以下的 代码通过System.IO namespace,显示如何从一个文本文件中读和写:
// Create and write to a text file
StreamWriter sw = new StreamWriter("text.txt");
sw.WriteLine("Hello, World!");
sw.Close();
// Read and display a text file
StreamReader sr = new StreamReader("text.txt");
Console.WriteLine(sr.ReadToEnd());
sr.Close();
Table 1-5 通用的流类型:
System.IO 类型 用于
FileStream 创建一个基本流用于去写文件和读文件
MemoryStream 创建一个基本流在内存中读和写
StreamReader 从流中读数据
StreamWriter 向流中写数据
如何抛出和捕捉异常:
异常是不期望的事件,它们干扰了一个assembly 的正常执行。比如,如果你的assembly 正在从一个不存在的硬盘中读大量的文本,runtime 将会抛出一个异常。异常的发生是明智的,因为这样你的assembly无法进行运行。在以前的例子中,你能通知使用者文件不能用,然后等待着来自使用者的命令。简单的例子如下:
try
{
StreamReader sr = new StreamReader(@"C:"boot.ini");
Console.WriteLine(sr.ReadToEnd());
}
catch (Exception ex)
{
// If there are any problems reading the file, display an error message
Console.WriteLine("Error reading file: " + ex.Message);
}
在上面的例子中,如果有任何错误发生—包括一个文件找不到了,没有充足的权限,或在读文件时有错误—这时执行会在Catch块中继续执行下去。
如果没有问题发生,运行时将会忽略Catch块。基本的Exception类非常有用并且包含了一个错误信息和其它的应用数据。除了基本的Exception类,Framework还定义了几百个Exception类去描述不同的事件,所有的异常类都继承System.SystemException。另外,当你需要比标准异常更多的细节去描述一个事件定义你的异常时,你可以通过继承System.ApplicationException或System. Exception去定义自己的异常。
定义多个异常类许可你对不同的错误作出不同的反应。如果第一个Catch 块符合异常类型,runtime只会运行第一个Catch 块,所以Catch应该按从“最特殊到最不特殊(most-specific to least-specific,从特殊到一般)”的次序排列下去。以下的代码为了一个找不到文件的错误显示不同的错误信息,一个权限不够的错误,和其它任何可能发生的错误:
' VB
Try
Dim sr As StreamReader = New StreamReader("text.txt")
Console.WriteLine(sr.ReadToEnd)
Catch ex As System.IO.FileNotFoundException
Console.WriteLine("The file could not be found.")
Catch ex As System.UnauthorizedAccessException
Console.WriteLine("You do not have sufficient permissions.")
Catch ex As Exception
Console.WriteLine("Error reading file: " + ex.Message)
End Try
这个过程有时被称为异常过滤。捕捉异常也支持Finally块。Finally块运行在Try块之后并且任何Catch块要进行这个结束执行,无论一个异常是否抛出。因此你应该使用一个Finally块去关闭任何流和清理任何被异常所打开的对象。以下的代码关闭了StreamReader对象,无论一个异常是否发生StreamReader sr = new StreamReader("text.txt");
try
{
Console.WriteLine(sr.ReadToEnd());
}
catch (Exception ex)
{
// If there are any problems reading the file, display an error message
Console.WriteLine("Error reading file: " + ex.Message);
}
finally
{
// Close the StreamReader, whether or not an exception occurred
sr.Close();
}
注意:StreamReader的声明移到了Try块之外。这时必须的,因为Finally块不能访问Try块内声明的变量。这样的设计是明智的,因为可能会引发的异常使Try块内声明的变量可能没有被执行。使用Try/Catch/Finally 块可以捕捉StreamReader声明时或之后的异常。当错误发生时,强健的错误处理提升了用户的经验并且简化了更改错误的过程。
异常处理引发了一个轻量级的性能损耗。
相关推荐
#### 第2章:生成、打包、部署和管理应用程序及类型 - **项目构建流程**:讨论.NET应用程序从源代码到可执行文件的构建过程。 - **程序集打包**:解释如何将多个组件打包成一个程序集,便于部署和管理。 - **版本...
第2章 生成、打包、部署和管理应用程序及类型 2.1 .NET Framework部署目标 2.2 将类型生成到模块中 2.2.1 响应文件 2.3 元数据概述 2.4 将模块合并成程序集 2.4.1 使用Visual Studio IDE将程序集添加到项目中...
.NET Framework是微软开发的一个软件框架,它为开发者提供了一个运行环境,用于构建、运行基于C#等编程语言的应用程序。这个框架集成了许多关键组件,包括公共语言运行库(CLR)、类库、以及用于Windows应用程序开发...
#### 第2章 C#概述 - **面向对象程序设计** - **封装**:封装是指将数据和操作数据的方法组合在一起,隐藏内部实现细节,只暴露必要的接口。 - **多态性**:多态性允许子类重写父类的方法,从而使得不同对象可以...
第2章 C# 3.0程序设计基础 2.1 C#程序 2.1.1 C#程序的结构 2.1.2 C# IDE的代码设置 2.2 变量 2.2.1 定义 2.2.2 值类型 2.2.3 引用类型 2.3 变量规则 2.3.1 命名规则和命名习惯 2.3.2 声明并初始化变量 2.3.3 数组 ...
**第一章:.NET编程语言C#** - **1.1 Microsoft .NET —— 一场新的革命** - **概念介绍**:.NET是微软于2000年提出的一项重大技术革新,旨在通过构建一个全新的、统一的技术平台,来解决互联网时代的软件开发问题...
##### 第一章 .NET编程语言C# **1.1 Microsoft .NET —— 一场新的革命** **1.1.1 什么是.NET** 2000年6月22日,对于微软公司乃至整个信息技术(IT)产业而言都是一个重要的日子。在这天,微软正式宣布了其面向...
##### 第一章:.NET编程语言C# - **1.1 Microsoft .NET —— 一场新的革命** - **概念**:介绍Microsoft .NET作为下一代计算平台的背景、愿景和发展方向。 - **背景**:提及2000年6月22日,微软宣布推出.NET框架...
##### 第一章:.NET编程语言C# - **1.1 Microsoft .NET —— 一场新的革命** - **1.1.1 什么是.NET** - .NET是微软在2000年推出的下一代计算平台和服务框架,旨在通过互联网提供各种服务。 - **概念**:.NET的...
#### 第一章:.NET编程语言C# - **Microsoft .NET**:这一章节介绍了Microsoft .NET的概念,它是微软提出的一种全新的计算平台和服务框架,旨在构建和运行跨设备、操作系统和网络的应用程序。 - **.NET与C#**:讨论...
《深入.NET平台和C#编程》是针对.NET开发者的一本深度学习资料,第九章可能涵盖了.NET框架的关键概念和C#编程语言的高级特性。在这个章节中,我们可以期待学习到以下重要知识点: 1. **.NET Framework**: .NET ...
第2章 生成、打包、部署和管理应用程序及类型 2.1 .NET Framework部署目标 2.2 将类型生成到模块中 2.2.1 响应文件 2.3 元数据概述 2.4 将模块合并成程序集 2.4.1 使用Visual Studio IDE将程序集添加到项目中 ...
第2章 生成、打包、部署和管理应用程序及类型 2.1 .NET Framework部署目标 2.2 将类型生成到模块中 2.2.1 响应文件 2.3 元数据概述 2.4 将模块合并成程序集 2.4.1 使用Visual Studio IDE将程序集添加到项目中...
第2章 生成、打包、部署和管理应用程序及类型 2.1 .NET Framework部署目标 2.2 将类型生成到模块中 2.2.1 响应文件 2.3 元数据概述 2.4 将模块合并成程序集 2.4.1 使用Visual Studio IDE将程序集添加到项目中...
#### 第2章 ASP.NET的游戏规则——ASP.NET网站开发基础 **一、选择题** 这部分的选择题涉及了ASP.NET网站开发的一些基础知识。 **二、填空题** 1. **继承、封装、多态**:这三个是面向对象编程的核心概念。 2. *...
2. **C#基础知识(第2章)** - 讲解了C#的基础语法,如变量、数据类型、流程控制等。 - 通过实际示例帮助读者理解如何构建简单的应用程序。 - 强调了编码规范和良好的编程习惯。 3. **对象与类型(第3章)** - ...
#### 第一章:基础知识 1. **.NET Framework简介** - .NET Framework是由微软公司开发的一套软件框架,旨在简化开发过程并提高应用程序的性能。 - 它包括公共语言运行时(CLR)和.NET Framework类库。 - CLR提供...