|
Как связаны пространства имен и сборкиПространство имен и сборка (файл, содержащий реализацию типа) могут быть не связаны. В частности, различные типы, принадлежащие одному пространству имен, могут быть реализованы в нескольких сборках. Например, тип System.IO.FileStream реализован в сборке MSCorLib.dll, a System.IO.FileSystemWatcher — в System.dll. В действительности сборка System.dll даже не поставляется в составе.NET Framework. В одной сборке могут содержаться типы из разных пространств имен. Так, в сборке MSCorLib.dll находятся типы System.Int32 и System.Text.StringBuilder. В документации.NET Framework SDK четко показано, к каким пространствам имен принадлежат те или иные типы и в каких сборках находятся реализации типов. На рис. 3.1 справа от раздела Syntax показано, что тип ResXFileRef относится к пространству имен SystemResources, однако его реализация находится в сборке System.Windows.Forms.dll. Чтобы скомпилировать код, ссылающийся на тип ResXFileRef следует добавить в код директиву using SystemResources; а также использовать параметр /r:System.Windows.Forms.dll компилятора.
Рис. 3.1. Сведения о пространстве имен и сборке для конкретного типа в документации к.NET Framework SDK Элементарные, ссылочные и значимые типы В этой главе речь идет о разновидностях типов, с которыми вы будете иметь дело при программировании для Microsoft.NET Framework. Важно, чтобы все разработчики четко осознавали разницу в их поведении. Приступая к изучению.NET Framework, я (Рихтер) толком не понимал, в чем разница между элементарными, ссылочными и значимыми типами, и поэтому невольно напустил в свой код трудно вылавливаемых «жучков» и снизил его эффективность. Надеюсь, мой опыт и объяснения разницы между этими типами помогут вам избавиться от лишней головной боли и повысить производительность работы. Элементарные типы в языках программирования Некоторые типы данных применяют так широко, что для работы с ними во многих компиляторах предусмотрен упрощенный синтаксис. Например, целую переменную можно создать так: System.Int32 а = new System.Int32();
Конечно, подобный синтаксис для объявления и инициализации целой переменной кажется громоздким. К счастью, многие компиляторы (включая С#) позволяют использовать и более простые выражения, например: int a = 0;
Такой код читается намного лучше, да и компилятор в обоих случаях генерирует идентичный IL-код для System.Int32. Типы данных, которые поддерживаются компилятором напрямую, называются элементарными типами (primitive types) и отображаются им в типы из библиотеки классов.NET Framework Class Library (FCL). Так, С#-типу int соответствует System.Int32. Значит, весь следующий код компилируется без ошибок и преобразуется в одинаковые команды IL (в приведенном коде регистр символов важен): int а = 0; // Самый удобный синтаксис System.Int32 a = 0; // Удобный синтаксис int a = new int(); // Неудобный синтаксис System.Int32 a = new System.Int32(); // Самый неудобный синтаксис
В табл. 4.1 представлены типы FCL и соответствующие им элементарные типы С#. В других языках типам, удовлетворяющим общеязыковой спецификации Common Language Specification (CLS), будут соответствовать аналогичные элементарные типы. Однако поддержка языком типов, не удовлетворяющих требованиям CLS, необязательна.
Табл. 4.1. Элементарные типы С# и соответствующие типы FCL
Иначе говоря, можно полагать, что компилятор С# автоматически предполагает, что во всех файлах исходного кода есть следующие директивы using: using sbyte = System.SByte; using byte = System.Byte; using short = System.Int16; using ushort = System.UInt16; using int = System.Int32; using uint = System.UInt32;
Я не могу согласиться со следующим утверждением из спецификации языка С#: «С точки зрения стиля программирования предпочтительней использовать ключевое слово, а не полное системное имя типа». Я стараюсь использовать имена типов FCL и избегать имен элементарных типов. На самом деле мне бы хотелось, чтобы имен элементарных типов не было совсем, а разработчики употребляли только имена FCL-типов. И вот почему. § Мне попадались разработчики, не понимавшие, что использовать в коде: string или String. В С# это не важно, так как ключевое слово string в точности преобразуется в FCL-тип System.String. § В С# long отображается в System.Int64, но в другом языке это может быть Int16 или Int32. Как известно, в C++ с управляемыми расширениями long трактуется как Int32. Если кто-то возьмется читать код, написанный на новом для себя языке, то назначение кода может быть неверно им истолковано. Многим языкам незнакомо ключевое слово long, и их компиляторы не пропустят код, где оно встречается. § У многих FCL-типов есть методы, в имена которых включены имена типов. Например, у типа BinaryReader есть методы ReadBoolean, ReadInt32 и ReadSingle и т. д., а у типа System.Convert — методы ToBoolean, Tolnt32 и ToSingle и т. д. Вот вполне приемлемый код, в котором строка, содержащая float, кажется мне неестественной, и сразу не очевидно, что код корректный: BinaryReader br = new BinaryReader(...); float val = br.ReadSingle(); /* Код правильный, но выглядит неестественно*/ Single val = br.ReadSingle(); /* Код правильный и выглядит нормально*/
По этим причинам я буду использовать в этой книге только имена FCL-типов. Скорее всего, следующий код во многих языках благополучно скомпилируется и выполнится: Int32 i=5; // 32-разрядное число Int64 I = i; // Неявное приведение типа к 64-разрядному значению
Но, если вспомнить, что говорилось о приведении типов ранее, можно решить, что он компилироваться не будет. Все-таки System.Int32 и System.Int64 — разные типы и не приводятся друг к другу. Могу вас обнадежить: код успешно компилируется и делает все, что ему положено. Объясню, почему. Дело в том, что компилятор С# неплохо разбирается в элементарных типах и применяет свои правила при компиляции кода. Иначе говоря, он распознает наиболее распространенные шаблоны программирования и генерирует такие IL-кoманды, благодаря которым исходный код работает, как требуется. В первую очередь это относится к приведению типов, литералам и операторам, примеры которых мы рассмотрим ниже. Начнем с того, что компилятор выполняет явное и неявное приведение между элементарными типами, например: Int32 i = 5; // Неявное приведение Int32 к Int32 Int64 I = i; // Неявное приведение Int32 к Int64 Single s = i; // Неявное приведение Int32 к Single Byte b = (Byte) i; // Явное приведение Int32 к Byte Int16 v = (Int16) s; // Явное приведение Single к Int16
С# разрешает неявное приведение типа, если это преобразование «безопасно», то есть не сопряжено с потерей данных; пример — преобразование из Int32 в Intб4. Однако для преобразования с риском потери данных С# требует явного приведения типа. Для числовых типов «небезопасное» преобразование означает «связанное с потерей точности или величины числа». Так, преобразование из Int32 в Byte требует явного приведения к типу, так как при больших величинах Int32 будет потеряна точность; требует приведения и преобразование из Single в Int64, поскольку число Single может оказаться больше, чем допустимо для Int64. Разные компиляторы могут создавать различный код для выполнения приведения. Например, в случае приведения числа 6.8 типа Single к типу Int32 одни компиляторы создадут код, который поместит в Int32 число 6, а другие округлят результат до 7. Между прочим, в С# дробная часть всегда отбрасывается. Точные правила приведения для элементарных типов вы найдете в разделе спецификаций языка С#, посвященном преобразованиям («Conversions»). Помимо приведения, компилятор знает и о другой особенности элементарных типов: к ним применима литеральная форма записи. Литералы сами по себе считаются экземплярами типа, поэтому можно вызывать экземплярные методы, например так: Console.WriteLine(123.ToString() + 456.ToString()); // "123456"
Кроме того, благодаря тому, что выражения, состоящие из литералов, вычисляются на этапе компиляции, возрастает скорость выполнения приложения. Boolean found = false; // В готовом коде found присваивается 0 Int32 х = 100 + 20 + 3; // В готовом коде х присваивается 123 String s = "а " + "be"; // В готовом коде s присваивается "a be"
И, наконец, компилятор знает, как и в каком порядке интерпретировать встретившиеся в коде операторы (в том числе +, -, *, /, %, &, л, |, ==,!=, >, <, >=, <=, «,», ~,!, ++, -- и т. п.): Int32 х = 100; // Оператор присваивания Int32 у = х + 23; // Операторы суммирования и присваивания Boolean LessThanFifty = (у < 50); // Операторы "меньше чем" и присваивания
Система охраняемых территорий в США Изучение особо охраняемых природных территорий(ООПТ) США представляет особый интерес по многим причинам... Конфликты в семейной жизни. Как это изменить? Редкий брак и взаимоотношения существуют без конфликтов и напряженности. Через это проходят все... ЧТО И КАК ПИСАЛИ О МОДЕ В ЖУРНАЛАХ НАЧАЛА XX ВЕКА Первый номер журнала «Аполлон» за 1909 г. начинался, по сути, с программного заявления редакции журнала... Что способствует осуществлению желаний? Стопроцентная, непоколебимая уверенность в своем... Не нашли то, что искали? Воспользуйтесь поиском гугл на сайте:
|