博文
C# 程序员参考--事件教程(2006-09-03 18:15:00)
摘要:本教程展示如何在 C# 中声明事件、调用事件和挂接到事件。
教程
C# 中的“事件”是当对象发生某些有趣的事情时,类向该类的客户提供通知的一种方法。事件最常见的用途是用于图形用户界面;通常,表示界面中的控件的类具有一些事件,当用户对控件进行某些操作(如单击某个按钮)时,将通知这些事件。
但是事件未必只用于图形界面。事件为对象提供一种通常很有用的方法来发出信号表示状态更改,这些状态更改可能对该对象的客户很有用。事件是创建类的重要构造块,这些类可在大量的不同程序中重复使用。
使用委托来声明事件。如果您尚未学习“委托教程”,您应先学习它,然后再继续。请回忆委托对象封装一个方法,以便可以匿名调用该方法。事件是类允许客户为其提供方法(事件发生时应调用这些方法)的委托的一种方法。事件发生时,将调用其客户提供给它的委托。
除声明事件、调用事件和与事件挂钩的示例以外,本教程还介绍下列主题:
事件和继承
接口中的事件
.NET Framework 指南
示例 1
下面的简单示例展示一个 ListWithChangedEvent 类,该类类似于标准的 ArrayList 类,而且,每当列表内容更改时,该类均调用 Changed 事件。这样一个通用用途的类可在大型程序中以多种方式使用。
例如,某字处理器可能包含打开的文档的列表。每当该列表更改时,可能需要通知字处理器中的许多不同对象,以便能够更新用户界面。使用事件,维护文档列表的代码不需要知道需要通知谁,一旦文档列表发生了更改,将自动调用该事件,正确通知每个需要通知的对象。使用事件提高了程序的模块化程度。// events1.cs
using System;
namespace MyCollections
{
using System.Collections;
// A delegate type for hooking up change notifications.
public delegate void ChangedEventHandler(object sender, EventArgs e);
// A class that works just like ArrayList, but sends event
// notificati......
C# 程序员参考--委托教程(2006-09-03 18:14:00)
摘要:本教程演示委托类型。它说明如何将委托映射到静态方法和实例方法,以及如何组合委托(多路广播)。
教程
C# 中的委托类似于 C 或 C++ 中的函数指针。使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与 C 或 C++ 中的函数指针不同,委托是面向对象、类型安全的,并且是安全的。
委托声明定义一种类型,它用一组特定的参数以及返回类型封装方法。对于静态方法,委托对象封装要调用的方法。对于实例方法,委托对象同时封装一个实例和该实例上的一个方法。如果您有一个委托对象和一组适当的参数,则可以用这些参数调用该委托。
委托的一个有趣且有用的属性是,它不知道或不关心自己引用的对象的类。任何对象都可以;只是方法的参数类型和返回类型必须与委托的参数类型和返回类型相匹配。这使得委托完全适合“匿名”调用。
注意 委托是在调用方的安全权限下运行而不是声明方的权限下运行。
此教程包括两个示例:
示例 1 展示如何声明、实例化和调用委托。
示例 2 展示如何组合两个委托。
此外,还讨论以下主题:
委托和事件
委托与接口
示例 1
下面的示例阐释声明、实例化和使用委托。BookDB 类封装一个书店数据库,它维护一个书籍数据库。它公开 ProcessPaperbackBooks 方法,该方法在数据库中查找所有平装书,并为每本书调用一个委托。所使用的 delegate 类型称为 ProcessBookDelegate。Test 类使用该类输出平装书的书名和平均价格。
委托的使用促进了书店数据库和客户代码之间功能的良好分隔。客户代码不知道书籍的存储方式和书店代码查找平装书的方式。书店代码也不知道找到平装书后将对平装书进行什么处理。// bookstore.cs
using System;
// A set of classes for handling a bookstore:
namespace Bookstore
{
using System.Collections;
// Describes a book in the book list:
public struct Book
{
......
C# 程序员参考--运算符重载教程(2006-09-03 18:13:00)
摘要:本教程演示用户定义的类如何重载运算符。
示例文件
请参见“运算符重载”示例以下载和生成本教程中讨论的示例文件。
教程
运算符重载允许为运算指定用户定义的运算符实现,其中一个或两个操作数是用户定义的类或结构类型。本教程包含两个示例。第一个示例展示如何使用运算符重载创建定义复数加法的复数类。第二个示例展示如何使用运算符重载实现三值的逻辑类型。
示例 1
本示例展示如何使用运算符重载创建定义复数加法的复数类 Complex。本程序使用 ToString 方法的重载显示数字的虚部和实部以及加法结果。// complex.cs
using System;
public struct Complex
{
public int real;
public int imaginary;
public Complex(int real, int imaginary)
{
this.real = real;
this.imaginary = imaginary;
}
// Declare which operator to overload (+), the types
// that can be added (two Complex objects), and the
// return type (Complex):
public static Complex operator +(Complex c1, Complex c2)
{
return new Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
}
// Override the ToString method to display an complex number in the suitable format:
public override string ToString()
{
return(String.Format("{0} + {1}i", real, imaginary));
}
public static void......
C# 程序员参考--用户定义的转换教程(2006-09-03 18:13:00)
摘要:本教程展示如何定义转换以及如何在类或结构之间使用转换。
示例文件
请参见“用户定义的转换”示例以下载和生成本教程中讨论的示例文件。
教程
C# 允许程序员在类或结构上声明转换,以便可以使类或结构与其他类或结构或者基本类型相互进行转换。转换的定义方法类似于运算符,并根据它们所转换到的类型命名。
在 C# 中,可以将转换声明为 implicit(需要时自动转换)或 explicit(需要调用转换)。所有转换都必须为 static,并且必须采用在其上定义转换的类型,或返回该类型。
本教程介绍两个示例。第一个示例展示如何声明和使用转换,第二个示例演示结构之间的转换。
示例 1
本示例中声明了一个 RomanNumeral 类型,并定义了与该类型之间的若干转换。// conversion.cs
using System;
struct RomanNumeral
{
public RomanNumeral(int value)
{
this.value = value;
}
// Declare a conversion from an int to a RomanNumeral. Note the
// the use of the operator keyword. This is a conversion
// operator named RomanNumeral:
static public implicit operator RomanNumeral(int value)
{
// Note that because RomanNumeral is declared as a struct,
// calling new on the struct merely calls the constructor
// rather than allocating an object on the heap:
return new RomanNumeral(value);
}
// Declare an explicit conversion from a ......
C# 程序员参考--索引属性教程(2006-09-03 18:12:00)
摘要:本教程展示如何实现使用索引属性的类。索引属性使您可以使用表示类似于数组的若干种不同事物的集合的类。学习本教程以前应完成索引器教程。
示例文件
请参见“索引属性”示例以下载和生成本教程中讨论的示例文件。
教程
假定您要编写一个 Document 类,该类封装非常长的文本章节。为能够方便地实现各种操作(如检查拼写),您可能希望以单词(以及字符)的虚拟数组形式查看文档。
下面的示例展示实现这种类的技术。对于每个“索引属性”,您定义一个嵌套类,该类包含对主类实例的反向引用。主类上的 readonly 字段提供对嵌套类(定义每个虚拟数组)的实例的访问。每个嵌套类定义一个索引器以及其他类似集合的方法(例如 Count 属性)。下面的示例针对“Words”和“Characters”展示这一点。
注意 请慎重使用该技术!仅在使用数组索引操作提供的抽象化能明确阐明使用您的类的代码,并且索引器同时具有“获取”(Get) 和“设置”(Set) 访问器时,才使用该模式。
示例
本示例中定义了 Document 类。使用 Words 和 Characters 这两个索引属性在 Document 对象上执行某些文本操作。// indexedproperty.cs
using System;
public class Document
{
// Type allowing the document to be viewed like an array of words:
public class WordCollection
{
readonly Document document; // The containing document
internal WordCollection(Document d)
{
document = d;
}
// Helper function -- search character array "text", starting at
// character "begin", for word number "wordCount.......
C# 程序员参考--索引器教程(2006-09-03 18:12:00)
摘要:本教程展示 C# 类如何声明索引器以提供对类的类似数组的访问。
示例文件
请参见“索引器”示例以下载和生成本教程中讨论的示例文件。
教程
定义“索引器”使您可以创建作为“虚拟数组”的类。该类的实例可以使用 [] 数组访问运算符进行访问。在 C# 中定义索引器类似于在 C++ 中定义运算符 [],但前者灵活得多。对于封装类似数组的功能或类似集合的功能的类,使用索引器使该类的用户可以使用数组语法访问该类。
例如,假定您想定义一个类,该类使文件显示为字节数组。如果文件非常大,则将整个文件读入内存是不切实际的,尤其在您只想读取或更改少数字节时。通过定义 FileByteArray 类,您可使文件外观类似于字节数组,但读或写字节时,实际执行的是文件的输入和输出。
除下面的示例以外,本教程中还讨论有关“创建索引属性”的高级主题。
示例
本示例中,FileByteArray 类使得像字节数组那样访问文件成为可能。Reverse 类反转文件的字节。可以运行该程序以反转任何文本文件的字节,包括程序源文件本身。若要将反转的文件更改回正常状态,请在同一文件上再次运行该程序。// indexer.cs
// arguments: indexer.txt
using System;
using System.IO;
// Class to provide access to a large file
// as if it were a byte array.
public class FileByteArray
{
Stream stream; // Holds the underlying stream
// used to access the file.
// Create a new FileByteArray encapsulating a particular file.
public FileByteArray(string fileName)
{
stream = new FileStream(fileName, FileMode.Open);
}
// Close the stream. This shou......
C# 程序员参考--结构教程(2006-09-03 18:11:00)
摘要:本教程介绍结构的语法和用法。它还涉及类与结构之间的重大差异。
示例文件
请参见“结构”示例以下载和生成本教程中讨论的示例文件。
教程
此教程包括两个示例。第一个示例向您展示如何声明和使用结构,而第二个示例演示向方法传递实例时结构和类之间的差异。还向您介绍下列主题:
结构与类
堆还是堆栈?
构造函数和继承
结构上的属性
示例 1
本示例声明一个结构,它有三个成员:一个属性、一个方法和一个私有字段。本示例创建该结构的一个实例,并将其投入使用:// struct1.cs
using System;
struct SimpleStruct
{
private int xval;
public int X
{
get
{
return xval;
}
set
{
if (value < 100)
xval = value;
}
}
public void DisplayX()
{
Console.WriteLine("The stored value is: {0}", xval);
}
}
class TestClass
{
public static void Main()
{
SimpleStruct ss = new SimpleStruct();
ss.X = 5;
ss.DisplayX();
}
}
输出The stored value is: 5
结构与类
结构可能看似类,但存在一些重要差异,应引起注意。首先,类为引用类型,而结构为值类型。使用结构,您可以创建行为类似内置类型的对象,同时享有它们的好处。
堆还是堆栈?
在类上调用“新建”(New) 运算符时,它将在堆上进行分配。但是,当实例化结构时,将在堆栈上创建结构。这样将产生性能增益。而且,您不会像对待类那样处理对结构实例的引用。您将直接对结构实例进行操作。鉴于此原因,向方法传......
C# 程序员参考--集合类教程(2006-09-03 18:10:00)
摘要:本教程展示如何实现可与 foreach 语句一起使用的集合类。
教程
foreach 语句是循环访问数组元素的方便方法。如果集合类已实现 System.Collections.IEnumerator 和 System.Collections.IEnumerable 接口,它还可以枚举该集合的元素。
示例 1
下面的代码示例阐释如何编写可与 foreach 一起使用的集合类。该类是字符串标记化拆分器,类似于 C 运行时库函数 strtok。// tokens.cs
using System;
// The System.Collections namespace is made available:
using System.Collections;
// Declare the Tokens class:
public class Tokens : IEnumerable
{
private string[] elements;
Tokens(string source, char[] delimiters)
{
// Parse the string into tokens:
elements = source.Split(delimiters);
}
// IEnumerable Interface Implementation:
// Declaration of the GetEnumerator() method
// required by IEnumerable
public IEnumerator GetEnumerator()
{
return new TokenEnumerator(this);
}
// Inner class implements IEnumerator interface:
private class TokenEnumerator : IEnumerator
{
private int position = -1;
private Tokens t;
public TokenEnumera......
C# 程序员参考--版本控制教程(2006-09-03 18:08:00)
摘要:本教程使用 override 和 new 关键字来演示 C# 中的版本控制。版本控制在基类和派生类衍生时维护它们之间的兼容性。
教程
C# 语言被设计为不同库中的基类和派生类之间的版本控制可以衍生,并保持向后兼容。例如,这意味着在基类中引入与派生类中的某个成员名称相同的新成员不是错误。它还意味着类必须显式声明某方法是要重写一个继承方法,还是一个仅隐藏具有类似名称的继承方法的新方法。
在 C# 中,默认情况下方法不是虚拟的。若要使方法成为虚拟方法,必须在基类的方法声明中使用 virtual 修饰符。然后,派生类可以使用 override 关键字重写基虚拟方法,或使用 new 关键字隐藏基类中的虚拟方法。如果 override 关键字和 new 关键字均未指定,编译器将发出警告,并且派生类中的方法将隐藏基类中的方法。下面的示例在实际操作中展示这些概念。
示例// versioning.cs
// CS0114 expected
public class MyBase
{
public virtual string Meth1()
{
return "MyBase-Meth1";
}
public virtual string Meth2()
{
return "MyBase-Meth2";
}
public virtual string Meth3()
{
return "MyBase-Meth3";
}
}
class MyDerived : MyBase
{
// Overrides the virtual method Meth1 using the override keyword:
public override string Meth1()
{
return "MyDerived-Meth1";
}
// Explicitly hide the virtual method Meth2 using the new
// keyword:
public new string Meth2()
{
return "MyDer......
C# 程序员参考--库教程(2006-09-03 18:08:00)
摘要:本教程展示如何在 C# 中创建和使用库。
教程
本教程演示如何使用必要的编译器选项创建托管 DLL 文件,以及如何通过客户程序使用该库。
示例
本示例使用下列模块:
DLL 库 (Functions.dll),从以下源文件生成:
Factorial.cs:计算并返回某数的阶乘。
DigitCounter.cs:计算所传递的字符串中的数字位数。
使用该 DLL 的客户程序 (FunctionTest.exe),从源文件 FunctionClient.cs 进行编译。本程序显示输入参数的阶乘。
生成库
若要生成该库,请将 Functions 作为当前目录,并在命令提示处键入下列内容:csc /target:library /out:Functions.dll Factorial.cs DigitCounter.cs
其中:
/target:library
指定输入文件是个 DLL,而不是可执行文件(这还将阻止编译器查找默认入口点)。
/out:Functions.dll
指定输出文件名是 Functions.dll。通常,输出名与命令行上的第一个 C# 源文件(本示例中为 Factorial)的名称相同。
Factorial.cs DigitCounter.cs
指定要编译并放入 DLL 的文件。
编译客户程序
若要编译该程序,请将 FunctionTest 作为当前目录,并在命令提示处键入下列内容:copy ..\Functions\Functions.dll .
csc /out:FunctionTest.exe /R:Functions.DLL FunctionClient.cs
其中:
/out:FunctionTest.exe
指定输出文件名为 FunctionTest.exe。
/R:Functions.DLL
指定解析引用时必须包括 Functions.DLL。该 DLL 必须位于当前目录中,或具有完全限定的路径。
FunctionClient.cs
指定客户源代码。
这将创建可执行文件 FunctionTest.exe。
文件 1:Factorial.cs
以下代码计算传递给此方法(与“库”示例不同,请将此方法编译到库中)的整数......