Chapter 1: 使用引用类型

这是系列文章,是读"MS Press - MCTS Self-Paced Training Kit"的笔记,并把主要部分翻译成中文。
作者地址:http://www.cnblogs.com/nevernet (转载请保留)

Lesson 2: 使用引用类型

.NET Framework里面大部分类型都是引用类型,它们提供了极好的性能。


什么是引用类型?

引用类型仅仅保存它们数据的地址(译者注:在内存里的地址), 就如通常所说的指针( pointer), 在栈( stack)中. 地址所指向的实际数据,其实是保存在名叫堆(heap)的某块内存中。运行环境通过垃圾回收机制(garbage collection)来管理使用的内存(the memory used by the heap)。


Best Practices—Garbage collection 

Garbage collection occurs only when needed or when triggered by a call to GC.Collect. Automatic garbage collection is optimized for applications where most instances are short-lived, except for those allocated at the beginning of the application. Following that design pattern will result in the best performance.

比较引用类型和值类型的行为

因为引用类型表示的是数据的地址,而不是数据本身,所以当把一个引用变量赋值给另外一个变量时,并没有复制数据,相反,仅仅是复制了引用(译者注:仅仅是复制了这个数据的地址)实际上,他们指向的数据都是一样的.

考虑下面的代码片段:

' VB
Structure Numbers
Public val As Integer

Public Sub New(ByVal _val As Integer)
val = _val
End Sub

Public Overloads Overrides Function ToString() As String
Return val.ToString
End Function
End Structure

// C#
struct Numbers
{
public int val;

public Numbers(int _val)
{ val = _val; }

public override string ToString()
{ return val.ToString(); }
}

现在再考虑如下代码:

' VB
Dim n1 As Numbers = New Numbers(0)
Dim n2 As Numbers = n1
n1.val += 1
n2.val += 2
Console.WriteLine("n1 = {0}, n2 = {1}", n1, n2)

// 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" ,因为它们是值类型,  但是,当你更改一个引用类型时, 将会更改所有该引用类型的副本(copies).

内建引用类型

在 .NET Framework里面,有大约2500种内建引用类型.  Table 1-3 列出了经常使用的类型

Table 1-3: Common Reference Types

Type

Use for

System.Object

The Object type is the most general type in the Framework. You can convert any type to System.Object, and you can rely on any type having ToString, GetType, and Equals members inherited from this type.

System.String

Text data.

System.Text.StringBuilder

Dynamic text data.

System.Array

Arrays of data. This is the base class for all arrays. Array declarations use language-specific array syntax.

System.IO.Stream

Buffer for file, device, and network I/O. This is an abstract base class; task-specific classes are derived from Stream.

System.Exception

Handling system and application-defined exceptions. Task-specific exceptions inherit from this type.

Strings and String Builders

这个两个类型不仅仅只是一个装数据的容器,也提供一些方法来操作数据,如下:

' VB
Dim s As String = "this is some text to search"
s = s.Replace("search", "replace")
Console.WriteLine(s)

// C#
string s = "this is some text to search";
s = s.Replace("search", "replace");
Console.WriteLine(s);

在 .NET里面,System.String类型的变量是不可变的. 在运行的时候,如果赋值给一个string类型的变量,将复制一个新的值(与值类型的行为相同)

' VB
Dim s As String

s = "wombat" ' "wombat"
s += " kangaroo" ' "wombat kangaroo"
s += " wallaby" ' "wombat kangaroo wallaby"
s += " koala" ' "wombat kangaroo wallaby koala"
Console.WriteLine(s)

// C#
string s;

s = "wombat"; // "wombat"
s += " kangaroo"; // "wombat kangaroo"
s += " wallaby"; // "wombat kangaroo wallaby"
s += " koala"; // "wombat kangaroo wallaby koala"
Console.WriteLine(s);

只有最后一个字符串有引用; 其它三个将被垃圾回收机制回收. 减少这样的操作(因为这种操作将多次调用垃圾回收机制),将有助于提高性能. 其它办法如下:

  • String类来合并、加入、格式化等方法把多个items变成一个.

  • StringBuilder类 创建动态字符串.

StringBuilder 方案提供了很大的灵活性.下面是使用StringBuilder的演示:

' VB
Dim sb As New System.Text.StringBuilder(30)
sb.Append("wombat") ' Build string.
sb.Append(" kangaroo")
sb.Append(" wallaby")
sb.Append(" koala")
Dim s as String = sb.ToString ' Copy result to string.
Console.WriteLine(s)

// C#
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 运算符重载. Table 1-4 lists the operators the String class overrides.

Table 1-4: String Operators

Operator

Visual Basic

C#

Action on System.String

Addition

+ or &

+

Joins two strings to create a new string.

Equality

=

= =

Returns True if two strings have the same contents; False if they are different.

Inequality

<>

!=

The inverse of the equality operator.

Assignment

=

=

Copies the contents of one string into a new one. This causes strings to behave like value types, even though they are implemented as reference types.

如何创建数组并排序?

Arrays 使用 圆括号 (in Visual Basic) 或者方括号 (in C#) 来定义的:

' VB
' Declare and initialize an array.
Dim ar() As Integer = {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))

// 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]);

如何使用 Streams?

Streams are another very common type because they are the means for reading from and writing to the disk and communicating across the network. The System.IO.Stream type is the base type for all task-specific stream types. Table 1-5 shows some of the most commonly used stream types. 另外, network streams 可以在 System.Network.Sockets 命名空间下找到,  encrypted streams 可以在System.Security.Cryptography 命名空间下找到。

Table 1-5: Common Stream Types

System.IO Type

Use to

FileStream

Create a base stream used to write to or read from a file

MemoryStream

Create a base stream used to write to or read from memory

StreamReader

Read data from the stream

StreamWriter

Write data to the stream

最简单的 stream 类是StreamReaderStreamWriter, 它们支持你对文本文件的读写. You can pass a filename as part of the constructor, enabling you to open a file with a single line of code. After you have processed a file, call the Close method so that the file does not remain locked. The following code, which requires the System.IO namespace, 下面代码演示如何读写文本文件:

' VB
' Create and write to a text file
Dim sw As StreamWriter = New StreamWriter("text.txt")
sw.WriteLine("Hello, World!")
sw.Close

' Read and display a text file
Dim sr As StreamReader = New StreamReader("text.txt")
Console.WriteLine(sr.ReadToEnd)
sr.Close

// C#
// 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();


More Info—Streams 

更多关于streams的信息, refer to Chapter 2, "Input/Output (I/O)."

如何抛出和捕捉 Exceptions

Exceptions are unexpected events that interrupt normal execution of an assembly. For example, if your assembly is reading a large text file from a removable disk and the user removes the disk, the runtime will throw an exception. This makes sense because there is no way your assembly could continue running.

Exceptions should never cause your assembly to fail completely.相反 , 你应该计划异常何时发生, 捕捉它们, 并对这个事件做出回应. In the preceding example, you could notify the user that the file was not available, and then await further instructions from the user. The following simplified code, which requires the System.IO namespace, demonstrates this:

' VB
Try
Dim sr As StreamReader = New StreamReader("C:\boot.ini")
Console.WriteLine(sr.ReadToEnd)
Catch ex As Exception
' If there are any problems reading the file, display an error message
Console.WriteLine("Error reading file: " + ex.Message)
End Try

// C#
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);
}

In the preceding example, if any type of error occurs—including a file not found error, insufficient privileges error, or an error during the reading of the file—processing continues within the Catch block. If no problems occur, the runtime skips the Catch block.

The base Exception class is very useful and contains an error message and other application data. In addition to the base Exception class, the Framework defines hundreds of exception classes to describe different types of events, all derived from System.SystemException. Additionally, you can define your own exceptions when you need to describe an event in more detail than allowed by the standard exception classes by deriving from System.ApplicationException.

拥有不同的异常类,允许你响应不同类型的错误. 运行环境将只执行第一个匹配异常类型的 Catch 块, however, so order Catch blocks from the most-specific to the least-specific. The following code sample displays different error messages for a file not found error, an insufficient privileges error, and any other type of error that might occur:

' 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

This process is sometimes called filtering exceptions. Exception handling also supports a Finally block. The Finally block runs after the Try block and any Catch blocks have finished executing, whether or not an exception was thrown. Therefore, you should use a Finally block to close any streams or clean up any other objects that might be left open if an exception occurs. The following code sample closes the StreamReader object whether or not an exception occurs:

' VB
Dim sr As StreamReader = New StreamReader("text.txt")
Try
Console.WriteLine(sr.ReadToEnd)

Catch ex As Exception
' 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
End Try

// C#
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();
  //(译者注:其实把sr定义到try外面并不好,我们关闭sr时,可以加一个判断,如下:
  //if(sr != null) sr.close();
  //)
}

Notice that the StreamReader declaration was moved outside the Try block in the preceding example. This is necessary because the Finally block cannot access variables that are declared within the Try block. This makes sense because depending on where an exception occurred, variable declarations within the Try block might not yet have been executed. To catch exceptions that occur both during and after the StreamReader declaration, use nested Try/Catch/Finally blocks.

Typically, all code except for simple variable declarations should occur within Try blocks. Robust error handling improves the user experience when problems occur and greatly simplifies debugging problems. However, exception handling does incur a slight performance penalty. To conserve space and focus on specific topics, sample code within this book will typically not include exception handling.

Working with Reference Types

练习 1: 区分类型是值类型还是引用类型
Image from book

In this exercise, you will write a console application that displays whether objects are value or reference types.

  1. Using Visual Studio, create a new console application project. Name the project List-Value-Types.

  2. Create instances of the following classes:

    • SByte

    • Byte

    • Int16

    • Int32

    • Int64

    • String

    • Exception

    The following code demonstrates this:

    ' VB
    Dim a As SByte = 0
    Dim b As Byte = 0
    Dim c As Int16 = 0
    Dim d As Int32 = 0
    Dim e As Int64 = 0
    Dim s As String = ""
    Dim ex As Exception = New Exception

    // C#
    SByte a = 0;
    Byte b =0;
    Int16 c = 0;
    Int32 d = 0;
    Int64 e = 0;
    string s = "";
    Exception ex = new Exception();
  3. Add each of the instances to a new object array, as the following code demonstrates:

    ' VB
    Dim types As Object() = {a, b, c, d, e, s, ex}

    // C#
    object[] types = { a, b, c, d, e, s, ex };

  4. Within a foreach loop, check the object.GetType().IsValueType property to determine whether the type is a value type. Display each type name and whether it is a value type or a reference type, as the following code demonstrates:

    ' VB
    For Each o As Object In types
    Dim type As String
    If o.GetType.IsValueType Then
    type = "Value type"
    Else
    type = "Reference Type"
    End If
    Console.WriteLine("{0}: {1}", o.GetType, type)
    Next

    // C#
    foreach ( object o in types )
    {
    string type;
    if (o.GetType().IsValueType)
    type = "Value type";
    else
    type = "Reference Type";

    Console.WriteLine("{0}: {1}", o.GetType(), type);
    }
  5. Run the console application, and verify that each type matches your understanding.

Image from book

练习 2: Work with Strings and Arrays
Image from book

在这里练习里面,你将编写一个函数来排序字符串.

  1. Using Visual Studio, create a new console application project. Name the project SortString.

  2. Define a string. Then use the String.Split method to separate the string into an array of words. The following code demonstrates this:

    ' VB
    Dim s As String = "Microsoft .NET Framework 2.0 Application Development Foundation"
    Dim sa As String() = s.Split(" ")

    // C#
    string s = "Microsoft .NET Framework 2.0 Application Development Foundation";
    string[] sa = s.Split(' ');

  3. Call the Array.Sort method to sort the array of words, as the following code demonstrates:

    ' VB
    Array.Sort(sa)

    // C#
    Array.Sort(sa);
  4. Call the String.Join method to convert the array of words back into a single string, and then write the string to the console. The following code sample demonstrates this:

    ' VB
    s = String.Join(" ", sa)
    Console.WriteLine(s)

    // C#
    s = string.Join(" ", sa);
    Console.WriteLine(s);
  5. Run the console application, and verify that it works correctly.

Image from book
 
练习3: Work with Streams and Exceptions
Image from book

Consider a scenario(情节) in which a coworker wrote a simple Windows Forms application to view text files. However, users complain that it is very temperamental(喜怒无常的). If the user mistypes the filename or if the file is not available for any reason, the application fails with an unhandled exception error. You must add exception handling to the application to display friendly error messages to users if a file is not available.

  1. Copy the Chapter01\Lesson2-ViewFile folder from the companion CD to your hard disk, and open either the C# version or the Visual Basic .NET version of the ViewFile project.

  2. Exceptions occur when users attempt to view a file. Therefore, edit the code that runs for the showButton.Click event. Add code to catch any type of exception that occurs, and display the error in a dialog box to the user. If an exception occurs after the TextReader object is initialized, you should close it whether or not an exception occurs. You will need two nested Try blocks: one to catch exceptions during the TextReader initialization, and a second one to catch exceptions when the file is read. The following code sample demonstrates this:

    ' VB
    Try
    Dim tr As TextReader = New StreamReader(locationTextBox.Text)
    Try
    displayTextBox.Text = tr.ReadToEnd
    Catch ex As Exception
    MessageBox.Show(ex.Message)
    Finally
    tr.Close()
    End Try
    Catch ex As Exception
    MessageBox.Show(ex.Message)
    End Try

    // C#
    try
    {
    TextReader tr = new StreamReader(locationTextBox.Text);
    try
    { displayTextBox.Text = tr.ReadToEnd(); }
    catch (Exception ex)
    { MessageBox.Show(ex.Message); }
    finally
    { tr.Close(); }
    }
    catch (Exception ex)
    { MessageBox.Show(ex.Message); }
  3. Run your application. First verify that it can successfully display a text file. Then provide an invalid filename, and verify that a message box appears when an invalid filename is provided.

  4. Next add overloaded exception handling to catch System.IO.FileNotFoundException and System.UnauthorizedAccessException. The following code sample demonstrates this:

    ' VB
    Try
    Dim tr As TextReader = New StreamReader(locationTextBox.Text)
    Try
    displayTextBox.Text = tr.ReadToEnd
    Catch ex As Exception
    MessageBox.Show(ex.Message)
    Finally
    tr.Close()
    End Try
    Catch ex As System.IO.FileNotFoundException
    MessageBox.Show("Sorry, the file does not exist.")
    Catch ex As System.UnauthorizedAccessException
    MessageBox.Show("Sorry, you lack sufficient privileges.")
    Catch ex As Exception
    MessageBox.Show(ex.Message)
    End Try

    // C#
    try
    {
    TextReader tr = new StreamReader(locationTextBox.Text);
    try
    { displayTextBox.Text = tr.ReadToEnd(); }
    catch (Exception ex)
    { MessageBox.Show(ex.Message); }
    finally
    { tr.Close(); }
    }
    catch (System.IO.FileNotFoundException ex)
    { MessageBox.Show("Sorry, the file does not exist."); }
    catch (System.UnauthorizedAccessException ex)
    { MessageBox.Show("Sorry, you lack sufficient privileges."); }
    catch (Exception ex)
    { MessageBox.Show(ex.Message); }
  5. Run your application again, and verify that it provides your new error message if an invalid filename is provided.

Image from book

课程概要

  • 引用类型保存数据的地址而不是数据本身

  • 当你复制一个值类型时,这个值的副本已经建立了. 当你复制一个引用类型时, 仅仅复制了指针. 因此, 如果你复制了一个引用类型,并修改了这个副本, 那么这个副本和所有的原始数据都被修改了.

  • .NET Framework 包含了很多内建的引用类型,你可以直接使用,或者创建自己的引用类型.

  • Strings 是不可变的; StringBuilder 来创建动态的字符串.

  • 用streams 来读写 files, memory, 和 network.

  • Catch 来 过滤 exceptions . 在Finally 语句里面关闭所有资源。

课程复习


Answers 

Answers to these questions and explanations of why each answer choice is right or wrong are located in the "Answers" section at the end of the book.

1. 

Which of the following are reference types? (Choose all that apply.)

  1. Types declared Nullable

  2. String

  3. Exception

  4. All types derived from System.Object

Image from book

2. 

What is the correct order for Catch clauses when handling different exception types?

  1. Order from most general to most specific.

  2. Order from most likely to least likely to occur.

  3. Order from most specific to most general.

  4. Order from least likely to most likely to occur.

Image from book

3. 

When should you use the StringBuilder class instead of the String class?

  1. When building a string from shorter strings.

  2. When working with text data longer than 256 bytes.

  3. When you want to search and replace the contents of a string.

  4. When a string is a value type.

Image from book

4. 

Why should you close and dispose of resources in a Finally block instead of a Catch block?

  1. It keeps you from having to repeat the operation in each Catch.

  2. Finally blocks run whether or not an exception occurs.

  3. The compiler throws an error if resources are not disposed of in the Finally block.

  4. You cannot dispose of resources in a Catch block.

Image from book

Answers

1. 

Correct Answers: B and C

  1. Incorrect: Types declared as Nullable can only be value types.

  2. Correct: Strings are reference types.

  3. Correct: Exceptions are reference types.

  4. Incorrect: Value types derive from System.Object, so not all derived types are reference types.

2. 

Correct Answer: C

  1. Incorrect: You should order Catch clauses from most specific to most general.

  2. Incorrect: The first type that matches is caught and subsequent Catch clauses are skipped.

  3. Correct: The first type that matches is caught, and subsequent Catch clauses are skipped. Therefore, you should order Catch clauses from most specific to most general to enable you to catch errors that you have specific error-handling for, while still catching other exceptions with the more general Catch clauses.

  4. Incorrect: The first type that matches is caught and subsequent Catch clauses are skipped.

3. 

Correct Answer: A

  1. Correct: Using the String type to construct a dynamic string can result in a lot of temporary strings in memory because the String type is immutable. Therefore, using the StringBuilder class is preferable.

  2. Incorrect: Strings are limited to 32,767 bytes, not 256 bytes.

  3. Incorrect: You can search and replace with a standard String class.

  4. Incorrect: Strings are never value types; they are reference types.

4. 

Correct Answer: B

  1. Incorrect: While this statement is true, the real advantage of using a Finally block is that code is executed even if the runtime does not throw an exception. Code in a Catch block is executed only if an exception occurs.

  2. Correct: Use Finally blocks for code that should run whether or not an exception occurs.

  3. Incorrect: The compiler will not throw an error if you do not include a Finally block. Finally blocks are optional.

  4. Incorrect: You can dispose of resources in a Catch block. However, the code will run only if an exception occurs. Typically, you need to dispose of resources whether or not an exception occurs.

posted @ 2008-09-17 11:57  无尽思绪  阅读(285)  评论(0编辑  收藏  举报