Выбрать главу

// Продемонстрировать определения частичного класса. using System;

class Test { static void Main() { XY xy = new XY (1, 2); Console.WriteLine(xy.X + + xy.Y); } } Для того чтобы воспользоваться классом XY, необходимо включить в компиля цию все его файлы. Так, если файлы класса XY называются xy1.cs, ху2.cs и ху3.cs, а класс Test содержится в файле test.cs, то для его компиляции достаточно ввести в командной строке следующее.

csc test.cs xy1.cs xy2.cs хуЗ.cs И последнее замечание: в C# допускаются частичные обобщенные классы. Но пара метры типа в объявлении каждого такого класса должны совпадать с теми, что указы ваются в остальных его частях. ## Частичные методы Как пояснялось в предыдущем разделе, с помощью модификатора partial мож но создать класс частичного типа. Начиная с версии 3.0, в C# появилась возможность использовать этот модификатор и для создания частичного метода в элементе данных частичного типа. Частичный метод объявляется в одной его части, а реализуется в дру гой. Следовательно, с помощью модификатора partial можно отделить объявление метода от его реализации в частичном классе или структуре. Главная особенность частичного метода заключается в том, что его реализация не требуется! Если частичный метод не реализуется в другой части класса или структуры, то все его вызовы молча игнорируются. Это дает возможность определить, но не вос требовать дополнительные, хотя и не обязательные функции класса. Если эти функции не реализованы, то они просто игнорируются. Ниже приведена расширенная версия предыдущей программы, в которой создает ся частичный метод Show(). Этот метод вызывается другим методом, ShowXY(). Ради удобства все части класса XY представлены в одном файле, но они могут быть распре делены по отдельным файлам, как было показано в предыдущем разделе.

// Продемонстрировать применение частичного метода. using System;

partial class XY { public XY(int a, int b) { X = a; Y = b; } // Объявить частичный метод. partial void Show();

}

partial class XY { public int X { get; set; } // Реализовать частичный метод. partial void Show() { Console.WriteLine("{0}, {1}", X, Y); }

}

partial class XY { public int Y { get; set; } // Вызвать частичный метод. public void ShowXY() { Show(); }

}

class Test { static void Main() { XY xy = new XY(1, 2); xy.ShowXY(); } } Обратите внимание на то, что метод Show() объявляется в одной части класса XY, а реализуется в другой его части. В реализации этого метода выводятся значения ко ординат X и Y. Это означает, что когда метод Show() вызывается из метода ShowXY(), то данный вызов действительно имеет конкретные последствия: вывод значений координат X и Y. Но если закомментировать реализацию метода Show(), то его вызов из метода ShowXY() ни к чему не приведет. Частичным методам присущ ряд следующих ограничений. Они должны возвра щать значение типа void. У них не может быть модификаторов доступа и они не могут быть виртуальными. В них нельзя также использовать параметры out. ## Создание объектов динамического типа Как уже упоминалось не раз, начиная с главы 3, C# является строго типизирован ным языком программирования. Вообще говоря, это означает, что все операции про веряются во время компиляции на соответствие типов, и поэтому действия, не под держиваемые конкретным типом, не подлежат компиляции. И хотя строгий контроль типов дает немало преимуществ программирующему, помогая создавать устойчивые и надежные программы, он может вызвать определенные осложнения в тех случаях, когда тип объекта остается неизвестным вплоть до времени выполнения. Нечто по добное может произойти при использовании рефлексии, доступе к COM-объекту или же в том случае, если требуется возможность взаимодействия с таким динамическим языком, как, например, IronPython. До появления версии C# 4.0 подобные ситуации были трудноразрешимы. Поэтому для выхода из столь затруднительного положения в версии C# 4.0 был внедрен новый тип данных под названием dynamic. За одним важным исключением, тип dynamic очень похож на тип object, по скольку его можно использовать для ссылки на объект любого типа. А отличается он от типа object тем, что вся проверка объектов типа dynamic на соответствие типов откладывает до времени выполнения, тогда как объекты типа object подлежат этой проверке во время компиляции. Преимущество откладывания подобной проверки до времени выполнения состоит в том, что во время компиляции предполагается, что объект типа dynamic поддерживает любые операции, включая применение операто ров, вызовы методов, доступ к полям и т.д. Это дает возможность скомпилировать код без ошибок. Конечно, если во время выполнения фактический тип, присваиваемый объекту, не поддерживает ту или иную операцию, то возникнет исключительная си туация во время выполнения. В приведенном ниже примере программы применение типа dynamic демонстри руется на практике.

// Продемонстрировать применение типа dynamic. using System; using System.Globalization;

class DynDemo { static void Main() { // Объявить две динамические переменные. dynamic str; dynamic val; // Поддерживается неявное преобразование в динамические типы. // Поэтому следующие присваивания вполне допустимы. str = "Это строка"; val = 10; Console.WriteLine("Переменная str содержит: " + str); Console.WriteLine("Переменная val содержит: " + val + '\n'); str = str.ToUpper(CultureInfo.CurrentCulture); Console.WriteLine("Переменная str теперь содержит: " + str); val = val + 2; Console.WriteLine("Переменная val теперь содержит: " + val + '\n'); string str2 = str.ToLower(CultureInfo.CurrentCulture); Console.WriteLine("Переменная str2 содержит: " + str2); // Поддерживаются неявные преобразования из динамических типов. int х = val * 2; Console.WriteLine("Переменная x содержит: " + х); }

} Выполнение этой программы дает следующий результат.

Переменная str содержит: Это строка Переменная val содержит: 10

Переменная str теперь содержит: ЭТО СТРОКА Переменная val теперь содержит: 12

Переменная str2 содержит: это строка Переменная х содержит: 24 Обратите внимание в этой программе на две переменные str и val, объявляемые с помощью типа dynamic. Это означает, что проверка на соответствие типов операций с участием обеих переменных не будет произведена во время компиляции. В итоге для них оказывается пригодной любая операция. В данном случае для переменной str вызываются методы ToUpper() и ToLower() класса String, а переменная уча ствует в операциях сложения и умножения. И хотя все перечисленные выше действия совместимы с типами объектов, присваиваемых обеим переменным в рассматривае мом здесь примере, компилятору об этом ничего не известно — он просто принимает. И это, конечно, упрощает программирование динамических процедур, хотя и допу скает возможность появления ошибок в подобных действиях во время выполнения. В разбираемом здесь примере программа ведет себя "правильно" во время выпол нения, поскольку объекты, присваиваемые упомянутым выше переменным, поддер живают действия, выполняемые в программе. В частности, переменной val присваи вается целое значение, и поэтому она поддерживает такие целочисленные операции, как сложение. А переменной str присваивается символьная строка, и поэтому она поддерживает строковые операции. Следует, однако, иметь в виду, что ответственность за фактическую поддержку типом объекта, на который делается ссылка, всех операций над данными типа dynamic возлагается на самого программирующего. В противном случае выполнение программы завершится аварийным сбоем. В приведенном выше примере обращает на себя внимание еще одно обстоятель ство: переменной типа dynamic может быть присвоен любой тип ссылки на объект благодаря неявному преобразованию любого типа в тип dynamic. Кроме того, тип dynamic автоматически преобразуется в любой другой тип. Разумеется, если во время выполнения такое преобразование окажется неправильным, то произойдет ошибка при выполнении. Так, если добавить в конце рассматриваемой здесь программы сле дующую строку кода:

bool b = val; то возникнет ошибка при выполнении из-за отсутствия неявного преобразования типа int (который оказывается типом переменной val во время выполнения) в тип bool. Поэтому данная строка кода приведет к ошибке при выполнении, хотя она и будет скомпилирована безошибочно. Прежде чем оставить данный пример программы, попробуйте поэксперименти ровать с ней. В частности, измените тип переменных str и val на object, а затем попытайтесь скомпилировать программу еще раз. В итоге появятся ошибки при ком пиляции, поскольку тип object не поддерживает действия, выполняемые над обеи ми переменными, что и будет обнаружено во время компиляции. В этом, собственно, и заключается основное отличие типов object и dynamic. Несмотря на то что оба типа могут использоваться для ссылки на объект любого другого типа, над перемен ной типа object можно производить только те действия, которые поддерживаются типом object. Если же вы используете тип dynamic, то можете указать какое угодно действие, при условии что это действие поддерживается конкретным объектом, на ко торый делается ссылка во время выполнения. Для того чтобы стало понятно, насколько тип dynamic способен упростить реше ние некоторых задач, рассмотрим простой пример его применения вместе с рефлек сией. Как пояснялось в главе 17, чтобы вызвать метод для объекта класса, получаемого во время выполнения с помощью рефлексии, можно, в частности, обратиться к методу Invoke(). И хотя такой способ оказывается вполне работоспособным, нужный метод намного удобнее вызвать по имени в тех случаях, когда его имя известно. Например, вполне возможна такая ситуация, когда в некоторой сборке содержится конкретный класс, поддерживающий методы, имена и действия которых заранее известны. Но по скольку эта сборка подвержена изменениям, то приходится постоянно убеждаться в том, что используется последняя ее версия. Для проверки текущей версии сборки можно, например, воспользоваться рефлексией, сконструировать объект искомого класса, а за тем вызвать методы, определенные в этом классе. Теперь эти методы можно вызвать по имени с помощью типа dynamic, а не метода Invoke(), поскольку их имена известны. Разместите сначала приведенный ниже код в файле с именем MyClass.cs. Этот код будет динамически загружаться посредством рефлексии.