高级 C# 技术(C# 与 Java)

更新:2007 年 11 月

C# 提供了一些有用的语言功能(如索引器、属性和委托),这些功能支持高级编程技术。

索引器

索引器提供了一种像访问数组一样访问结构的方法。例如,可以有一个表示公司中的单个部门的类。这个类可能包含部门中所有雇员的姓名,而且索引器允许您访问这些姓名,如下所示:

sales[0] = "Nikki";
sales[1] = "Becky";

例如,在类定义中,通过用以下签名定义属性来启用索引器:

public string this [int index]  //indexer

然后,像对普通属性一样,为它提供 getset 方法,这些访问器指定当使用该索引器时将引用到什么内部成员。

在下面的简单示例中,创建一个名为 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

请参见

任务

“委托”示例

概念

C# 编程指南

参考

委托(C# 编程指南)

事件(C# 编程指南)

其他资源

C# 编程语言(针对 Java 开发人员)