Расширенные методики программирования на языке C# (сравнение C# и Java)

Обновлен: Ноябрь 2007

В 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 для оформления класса или метода.

В приведенном ниже примере метод GetTime помечается как веб-служба XML путем добавления атрибута WebMethodAttribute.

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, необходимый для вызова этой функции. При вызове этой веб-службы извлекается текущее значение данного свойства.

<?xml version="1.0" encoding="utf-8" ?>

<string xmlns="http://tempuri.org/">7:26 PM</string>

В следующем примере класс Employee помечен как сериализуемый с помощью добавления атрибута SerializableAttribute. Поле Salary помечено как открытое и не будет сериализовано из-за наличия атрибута 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

Вызов делегатов

Вызов делегатов осуществляется весьма просто. Нужно всего лишь подставить имя переменной делегата вместо имени метода. При этом будет вызван метод Add со значениями 11 и 22 и будет возвращен результат типа 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 и веб-приложениях. В отличие от обработки событий в языке Java, которая обычно выполняется путем реализации настраиваемых классов прослушивателя, разработчики C# для обработки событий могут использовать делегаты. Событие объявляется как поле типа делегата, при этом перед объявлением события используется ключевое слово event. События обычно объявляются открытыми, но можно использовать и любые другие модификаторы доступа. В следующем примере демонстрируется объявление 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