高级 C# 技术(C# 与 Java)
更新:2007 年 11 月
C# 提供了一些有用的语言功能(如索引器、属性和委托),这些功能支持高级编程技术。
索引器
索引器提供了一种像访问数组一样访问类或结构的方法。例如,可以有一个表示公司中的单个部门的类。这个类可能包含部门中所有雇员的姓名,而且索引器允许您访问这些姓名,如下所示:
sales[0] = "Nikki";
sales[1] = "Becky";
例如,在类定义中,通过用以下签名定义属性来启用索引器:
public string this [int index] //indexer
然后,像对普通属性一样,为它提供 get 和 set 方法,这些访问器指定当使用该索引器时将引用到什么内部成员。
在下面的简单示例中,创建一个名为 Department 的类,这个类使用索引器访问该部门中的雇员,这些雇员在内部表示为一个字符串数组:
public class Department
{
private string name;
private const int MAX_EMPLOYEES = 10;
private string[] employees = new string[MAX_EMPLOYEES]; //employee array
public Department(string departmentName) //constructor
{
name = departmentName;
}
public string this [int index] //indexer
{
get
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
return employees[index];
}
else
{
throw new System.IndexOutOfRangeException();
}
}
set
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
employees[index] = value;
}
else
{
throw new System.IndexOutOfRangeException();
}
}
}
// code for the rest of the class...
}
然后,可以创建这个类的一个实例并访问它,如下面的代码示例所示:
class TestDepartment
{
static void Main()
{
Department sales = new Department("Sales");
sales[0] = "Nikki";
sales[1] = "Becky";
System.Console.WriteLine("The sales team is {0} and {1}", sales[0], sales[1]);
}
}
输出为:
The sales team is Nikki and Becky
有关更多信息,请参见 索引器(C# 编程指南)。
属性
C# 提供了一种称为“属性”的机制,用于添加有关类型的声明性信息。属性有些类似于 Java 中的批注概念。关于类型的额外信息位于类型定义前面的声明标记中。下面的示例演示如何使用 .NET Framework 属性来修饰类或方法。
在下面的示例中,通过添加 WebMethodAttribute 属性将 GetTime 方法标记为 XML Web services。
public class Utilities : System.Web.Services.WebService
{
[System.Web.Services.WebMethod] // Attribute
public string GetTime()
{
return System.DateTime.Now.ToShortTimeString();
}
}
添加 WebMethod 属性使 .NET Framework 自动处理调用此函数所需的 XML/SOAP 交换。调用此 Web 服务将检索下面的值:
<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="http://tempuri.org/">7:26 PM</string>
在下面的示例中,通过添加 SerializableAttribute 属性将 Employee 类标记为 serializable。虽然 Salary 字段被标记为 public,但是它不会被序列化,因为它标记了 NonSerializedAttribute 属性。
[System.Serializable()]
public class Employee
{
public int ID;
public string Name;
[System.NonSerialized()] public int Salary;
}
有关更多信息,请参见 创建自定义属性(C# 编程指南)。
委托
C++、Pascal 和其他语言支持函数指针的概念,允许您在运行时选择要调用哪些函数。
Java 不提供任何具有函数指针功能的结构,但 C# 提供这种构造。通过使用 Delegate 类,委托实例可以封装属于可调用实体的方法。
对于实例方法,委托由一个包含类的实例和该实例上的方法组成。对于静态方法,可调用实体由一个类和该类上的静态方法组成。因此,委托可用于调用任何对象的函数,而且委托是面向对象的、类型安全和安全的。
定义和使用委托有三个步骤:
声明
实例化
调用
使用以下语法声明委托:
delegate void Del1();
然后可以使用此委托引用返回 void 而且不带任何参数的函数。
类似地,若要为任何带字符串参数且返回 long 型结果的函数创建委托,则使用以下语法:
delegate long Del2(string s);
然后,可以将此委托分配到带有此签名的任何方法,如下所示:
Del2 d; // declare the delegate variable
d = DoWork; // set the delegate to refer to the DoWork method
其中 DoWork 的签名为:
public static long DoWork(string name)
重新分配委托
Delegate 对象是不可变的,即设置与它们匹配的签名后就不能再更改签名了。但是,如果其他方法具有同一签名,您也可以指向该方法。在本例中,将 d 重新分配给一个新的委托对象,因此 d 将调用 DoMoreWork 方法。只有 DoWork 和 DoMoreWork 具有相同签名时,您才可以执行此操作。
Del2 d; // declare the delegate variable
d = DoWork; // set the delegate to refer to the DoWork method
d = DoMoreWork; // reassign the delegate to refer to the DoMoreWork method
调用委托
调用委托相当简单。只需用委托变量的名称替代方法名称。这将使用值 11 和 22 调用 Add 方法,并返回一个 long 型结果,该结果被赋给变量 sum:
Del operation; // declare the delegate variable
operation = Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // invoke the delegate
以下代码演示了委托的创建、实例化和调用:
public class MathClass
{
public static long Add(int i, int j) // static
{
return (i + j);
}
public static long Multiply (int i, int j) // static
{
return (i * j);
}
}
class TestMathClass
{
delegate long Del(int i, int j); // declare the delegate type
static void Main()
{
Del operation; // declare the delegate variable
operation = MathClass.Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // use the delegate to call the Add method
operation = MathClass.Multiply; // change the delegate to refer to the Multiply method
long product = operation(30, 40); // use the delegate to call the Multiply method
System.Console.WriteLine("11 + 22 = " + sum);
System.Console.WriteLine("30 * 40 = " + product);
}
}
输出
11 + 22 = 33
30 * 40 = 1200
委托实例必须包含一个对象引用。前面的示例通过将方法声明为静态(意味着无需指定对象引用)避开了这一要求。但如果委托指实例方法,则必须按如下方式给定对象引用:
Del operation; // declare the delegate variable
MathClass m1 = new MathClass(); // declare the MathClass instance
operation = m1.Add; // set the delegate to refer to the Add method
在本例中,Add 和 Multiply 是 MathClass 的实例方法。如果 MathClass 的方法未声明为静态方法,那么您可以使用 MathClass 的实例通过委托来调用这些方法,如下所示:
public class MathClass
{
public long Add(int i, int j) // not static
{
return (i + j);
}
public long Multiply (int i, int j) // not static
{
return (i * j);
}
}
class TestMathClass
{
delegate long Del(int i, int j); // declare the delegate type
static void Main()
{
Del operation; // declare the delegate variable
MathClass m1 = new MathClass(); // declare the MathClass instance
operation = m1.Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // use the delegate to call the Add method
operation = m1.Multiply; // change the delegate to refer to the Multiply method
long product = operation(30, 40); // use the delegate to call the Multiply method
System.Console.WriteLine("11 + 22 = " + sum);
System.Console.WriteLine("30 * 40 = " + product);
}
}
输出
本示例提供的输出与之前方法声明为静态的示例相同。
11 + 22 = 33
30 * 40 = 1200
委托和事件
.NET Framework 还可以广泛地将委托用于事件处理任务,如 Windows 或 Web 应用程序中的按钮 Click 事件。Java 中的事件处理通常通过实现自定义侦听器类完成,而 C# 开发人员则可以利用委托处理事件。事件的声明类似于具有委托类型的字段,区别在于事件声明前面有 event 关键字。事件通常被声明为 public,但允许使用任何可访问性修饰符。下面的示例演示了 delegate 和 event 的声明。
// Declare the delegate type:
public delegate void CustomEventHandler(object sender, System.EventArgs e);
// Declare the event variable using the delegate type:
public event CustomEventHandler CustomEvent;
事件委托是多路广播的,这意味着它们可以对多个事件处理方法进行引用。通过维护事件的已注册事件处理程序列表,委托为引发事件的类担当事件发送器的角色。下面的示例演示如何为多个函数订阅事件。EventClass 类包含委托、事件和调用事件的方法。请注意调用事件只能从声明该事件的类内部进行。然后,TestEvents 类使用 += 运算符订阅事件,并使用 -= 运算符取消订阅。调用 InvokeEvent 方法时,它将激发事件,所有订阅了该事件的函数也同步激发,如下面的示例所示。
public class EventClass
{
// Declare the delegate type:
public delegate void CustomEventHandler(object sender, System.EventArgs e);
// Declare the event variable using the delegate type:
public event CustomEventHandler CustomEvent;
public void InvokeEvent()
{
// Invoke the event from within the class that declared the event:
CustomEvent(this, System.EventArgs.Empty);
}
}
class TestEvents
{
private static void CodeToRun(object sender, System.EventArgs e)
{
System.Console.WriteLine("CodeToRun is executing");
}
private static void MoreCodeToRun(object sender, System.EventArgs e)
{
System.Console.WriteLine("MoreCodeToRun is executing");
}
static void Main()
{
EventClass ec = new EventClass();
ec.CustomEvent += new EventClass.CustomEventHandler(CodeToRun);
ec.CustomEvent += new EventClass.CustomEventHandler(MoreCodeToRun);
System.Console.WriteLine("First Invocation:");
ec.InvokeEvent();
ec.CustomEvent -= new EventClass.CustomEventHandler(MoreCodeToRun);
System.Console.WriteLine("\nSecond Invocation:");
ec.InvokeEvent();
}
}
输出
First Invocation:
CodeToRun is executing
MoreCodeToRun is executing
Second Invocation:
CodeToRun is executing