Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Тем не менее с помощью операторов == и != можно проверить, содержит ли обнуляемый объект пустое значение. Например, следующая проверка вполне допустима идает истинный результат.if(upper == null) // ...
Если в логическом выражении участвуют два объекта типа bool?, то его результатможет иметь одно из трех следующих значений: true (истинное), false (ложное) илиnull (неопределенное). Ниже приведены результаты применения логических операторов & и | к объектам типа bool?.PQP | QP & Qtruenulltruenullfalsenullnullfalsenulltruetruenullnullfalsenullfalsenullnullnullnull
И наконец, если логический оператор ! применяется к значению типа bool?, которое является пустым (null), то результат этой операции будет неопределенным(null).Частичные типы
Начиная с версии 2.0, в C# появилась возможность разделять определение класса,структуры или интерфейса на две или более части с сохранением каждой из них в отдельном файле. Это делается с помощью контекстного ключевого слова partial. Всеэти части объединяются вместе во время компиляции программы.
Если модификатор partial используется для создания частичного типа, то онпринимает следующую общую форму:partial тип имя_типа { // ...
где имя_типа обозначает имя класса, структуры или интерфейса, разделяемого на части. Каждая часть получающегося частичного типа должна указываться вместе с модификатором partial.
Рассмотрим пример разделения простого класса, содержащего координаты XY, натри отдельных файла. Ниже приведено содержимое первого файла.
partial class XY { public XY(int a, int b) { X = a; Y = b; }}
Далее следует содержимое второго файла.partial class XY { public int X { get; set; }}
И наконец, содержимое третьего файла.partial class XY { public int Y { get; set; }}В приведенном ниже файле исходного текста программы демонстрируется применение класса XY.
// Продемонстрировать определения частичного класса.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 автоматически преобразуется в любой другой тип. Разумеется, если во времявыполнения такое преобразование окажется неправильным, то произойдет ошибкапри выполнении. Так, если добавить в конце рассматриваемой здесь программы следующую строку кода: