Полное руководство. С# 4.0
Шрифт:
В следующем фрагменте кода переменной balance присваивается значение 123.75. double? balance = 123.75; double currentBalance; currentBalance = balance ?? 0.0;
Теперь переменная currentBalance содержит значение 123.75 переменной balance.
И еще одно замечание: выражение в правой части оператора ?? вычисляется толь ко в том случае, если выражение в левой его части не содержит значение. Этот факт демонстрируется в приведенной ниже программе. // Применение оператора ?? using System; class NullableDemo2 { // Возвратить нулевой остаток. static double GetZeroBal { Console.WriteLine("В методе GetZeroBalO."); return 0.0; } static void Main { double? balance = 123.75; double currentBalance; // Здесь метод GetZeroBal не вызывается, поскольку // переменная balance содержит конкретное значение. currentBalance = balance ?? GetZeroBal; Console.WriteLine(currentBalance); } }
В этой программе метод GetZeroBal
Обнуляемые объекты могут использоваться в выражениях отношения таким же образом, как и соответствующие объекты необнуляемого типа. Но они должны под чиняться следующему дополнительному правилу: когда два обнуляемых объекта срав ниваются в операциях сравнения <, >, <= или >=, то их результат будет ложным, если любой из обнуляемых объектов оказывается пустым, т.е. содержит значение null. В качестве примера рассмотрим следующий фрагмент кода. byte? lower = 16; byte? upper = null;
// Здесь переменная lower определена, а переменная upper не определена. if(lower < upper) // ложно
В данном случае проверка того, что значение одной переменой меньше значения другой, дает ложный результат. Хотя это и не совсем очевидно, как, впрочем, и следую щая проверка противоположного характера. if(lower > upper) // .. также ложно!
Следовательно, если один или оба сравниваемых обнуляемых объекта оказываются пустыми, то результат их сравнения всегда будет ложным. Это фактически означает, что пустое значение (null) не участвует в отношении порядка.
Тем не менее с помощью операторов == и != можно проверить, содержит ли обну ляемый объект пустое значение. Например, следующая проверка вполне допустима и дает истинный результат. if(upper == null) // ...
Если в логическом выражении участвуют два объекта типа bool?, то его результат может иметь одно из трех следующих значений: true (истинное), false (ложное) или null (неопределенное). Ниже приведены результаты применения логических опера торов & и | к объектам типа bool?. P Q P | Q P & Q true null true null false null null false null true true null null false null false null null null null
И наконец, если логический оператор ! применяется к значению типа 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 содержит: " + х); }