Текст книги "C# 4.0: полное руководство"
Автор книги: Герберт Шилдт
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 56 (всего у книги 83 страниц)
Одну сборку можно сделать дружественной по отношению к другой. Такой сборке доступны закрытые члены дружественной ей сборки. Благодаря этому средству становится возможным коллективное использование членов выбранных сборок, причем эти члены не нужно делать открытыми. Для того чтобы объявить дружественную сборку, необходимо воспользоваться атрибутом InternalsVisibleTo
.
В заключение этой главы в частности и всей части I вообще будут вкратце представлены ключевые слова, определенные в C# и не упоминавшиеся в предыдущих главах данной книги.
Ключевое слов lock
Ключевое слово lock
используется при создании многопоточных программ. Подробнее оно рассматривается в главе 23, где речь пойдет о многопоточном программировании. Но ради полноты изложения ниже приведено краткое описание этого ключевого слова.
Программа на C# может состоять из нескольких потоков исполнения. В этом случае программа считается многопоточной, и отдельные ее части выполняются параллельно, т.е. одновременно и независимо друг от друга. В связи с такой организацией программы возникает особого рода затруднение, когда два потока пытаются воспользоваться ресурсом, которым можно пользоваться только по очереди. Для разрешения этого затруднения можно создать критический раздел кода, который будет одновременно выполняться одним и только одним потоком. И это делается с помощью ключевого слова lock
. Ниже приведена общая форма этого ключевого слова:
lock(obj) {
// критический раздел кода
}
где obj
обозначает объект, для которого согласуется блокировка кода. Если один поток уже вошел в критический раздел кода, то второму потоку придется ждать до тех пор, пока первый поток не выйдет из данного критического раздела кода. Когда же первый поток покидает критический раздел кода, блокировка снимается и предоставляется второму потоку. С этого момента второй поток может выполнять критический раздел кода.
–
ПРИМЕЧАНИЕ
Более подробно ключевое слово lock рассматривается в главе 23.
–
Ключевое слово readonly
Отдельное поле можно сделать доступным в классе только для чтения, объявив его как readonly
. Значение такого поля можно установить только с помощью инициализатора, когда оно объявляется или же когда ему присваивается значение в конструкторе. После того как значение доступного только для чтения поля будет установлено, оно не подлежит изменению за пределами конструктора. Следовательно, поле типа readonly
удобно для установки фиксированного значения с помощью конструктора. Такое поле можно, например, использовать для обозначения размера массива, который часто используется в программе. Допускаются как статические, так и нестатические поля типа readonly
.
–
ПРИМЕЧАНИЕ
Несмотря на кажущееся сходство, поля типа readonly
не следует путать с полями типа const
, которые рассматриваются далее в этой главе.
–
Ниже приведен пример применения поля с ключевым словом readonly
.
// Продемонстрировать применение поля с ключевым словом readonly.
using System;
class MyClass {
public static readonly int SIZE = 10;
}
class DemoReadOnly {
static void Main() {
int[] source = new int[MyClass.SIZE];
int[] target = new int[MyClass.SIZE];
// Присвоить ряд значений элементам массива source,
for(int i=0; i < MyClass.SIZE; i++)
source[i] = i;
foreach(int i in source)
Console.Write(i + " ");
Console.WriteLine();
// Перенести обращенную копию массива source в массив target.
for(int i = MyClass.SIZE-1, j = 0; i > 0; i–, j++)
target[j] = source[i];
foreach(int i in target)
Console.Write(i + " ");
Console.WriteLine();
// MyClass.SIZE = 100; // Ошибка!!! He подлежит изменению!
}
}
В данном примере поле MyClass.SIZE
инициализируется значением 10. После этого его можно использовать, но не изменять. Для того чтобы убедиться в этом, удалите символы комментария в начале последней строки приведенного выше кода и попробуйте скомпилировать его. В итоге вы получите сообщение об ошибке.
Ключевые слова const и volatile
Ключевое слово, или модификатор, const
служит для объявления полей и локальных переменных, которые нельзя изменять. Исходные значения таких полей и переменных должны устанавливаться при их объявлении. Следовательно, переменная с модификатором const
, по существу, является константой. Например, в следующей строке кода:
const int i = 10;
создается переменная i типа const
и устанавливается ее значение 10. Поле типа const
очень похоже на поле типа readonly
, но все же между ними есть отличие. Если поле типа readonly
можно устанавливать в конструкторе, то поле типа const
– нельзя.
Ключевое слово, или модификатор, volatile
уведомляет компилятор о том, что значение поля может быть изменено двумя или более параллельно выполняющимися потоками. В этой ситуации одному потоку может быть неизвестно, когда поле было изменено другим потоком. И это очень важно, поскольку компилятор C# будет автоматически выполнять определенную оптимизацию, которая будет иметь результат лишь в том случае, если поле доступно только одному потоку. Для того чтобы подобной оптимизации не подвергалось общедоступное поле, оно объявляется как volatile
.
Этим компилятор уведомляется о том, что значение поля типа volatile
следует получать всякий раз, когда к нему осуществляется доступ.
Оператор using
Помимо рассматривавшейся ранее директивы using
, имеется вторая форма ключевого слова using
в виде оператора. Ниже приведены две общие формы этого оператора:
using (obj) {
// использовать объект obj
}
using (тип obj = инициализатор) {
// использовать объект obj
}
где obj является выражением, в результате вычисления которого должен быть получен объект, реализующий интерфейс System.IDisposable
. Этот объект определяет переменную, которая будет использоваться в блоке оператора using
. В первой форме объект объявляется вне оператора using
, а во второй форме – в этом операторе. По завершении блока оператора using
для объекта obj вызывается метод Dispose()
, определенный в интерфейсе System.IDisposable
. Таким образом, оператор using
предоставляет средства, необходимые для автоматической утилизации объектов, когда они больше не нужны. Не следует, однако, забывать, что оператор using
применяется только к объектам, реализующим интерфейс System.IDisposable
.
В приведенном ниже примере демонстрируются обе формы оператора using
.
// Продемонстрировать применение оператора using.
using System;
using System.IO;
class UsingDemo {
static void Main() {
try {
StreamReader sr = new StreamReader(«test.txt»);
// Использовать объект в операторе using,
using(sr) {
// ...
}
} catch(IOException exc) {
// ...
}
try {
// Создать объект класса StreamReader в операторе using,
using(StreamReader sr2 = new StreamReader(«test.txt»)) {
// ...
}
} catch(IOException exc) {
// ...
}
}
}
В данном примере интерфейс IDisposable
реализуется в классе StreamReader
(посредством его базового класса TextReader
). Поэтому он может использоваться в операторе using
. По завершении этого оператора автоматически вызывается метод Dispose()
для переменной потока, закрывая тем самым поток.
Как следует из приведенного выше примера, оператор using
особенно полезен для работы с файлами, поскольку файл автоматически закрывается по завершении блока этого оператора, даже если он и завершается исключением. Таким образом, закрытие файла с помощью оператора using
зачастую упрощает код обработки файлов. Разумеется, применение оператора using
не ограничивается только работой с файлами. В среде .NET Framework имеется немало других ресурсов, реализующих интерфейс IDisposable
. И всеми этими ресурсами можно управлять с помощью оператора using
.
Ключевое слово extern
Ключевое слово extern
находит два основных применения. Каждое из них рассматривается далее по порядку.
Объявление внешних методов
В первом своем применении ключевое слово extern
было доступно с момента создания С#. Оно обозначает, что метод предоставляется в неуправляемом коде, который не является составной частью программы. Иными словами, метод предоставляется внешним кодом.
Для того чтобы объявить метод как внешний, достаточно указать в самом начале его объявления модификатор extern
. Таким образом, общая форма объявления внешнего метода выглядит следующим образом.
extern возвращаемый_тип имя_метода (список_аргументов) ;
Обратите внимание на отсутствие фигурных скобок.
В данном варианте ключевое слово extern
нередко применяется вместе с атрибутом DllImport
, обозначающим библиотеку DLL, в которой содержится внешний метод. Атрибут DllImport
принадлежит пространству имен System.Runtime.InteropServices
. Он допускает несколько вариантов, но, как правило, достаточно указать лишь имя библиотеки DLL, в которой содержится внешний метод. Вообще говоря, внешние методы следует программировать на С. (Если же это делается на C++, то имя внешнего метода может быть изменено в библиотеке DLL путем дополнительного оформления типов.)
Для того чтобы стало понятнее, как пользоваться внешними методами, обратимся к примеру конкретной программы, состоящей из двух файлов. Ниже приведен исходный код С из первого файла ExtMeth.с
, где определяется метод AbsMax()
.
#include
int __declspec(dllexport) AbsMax(int a, int b) {
return abs(a) < abs(b) ? abs(b) : abs(a);
}
В методе AbsMax()
сравниваются абсолютные значения двух его параметров и возвращается самое большое из них. Обратите внимание на обозначение __declspec(dllexport)
. Это специальное расширение языка С для программных средств корпорации Microsoft. Оно уведомляет компилятор о необходимости экспортировать метод AbsMax()
из библиотеки DLL, в которой он содержится. Для компилирования файла ExtMeth.с
в командной строке указывается следующее.
CL /LD /MD ExtMeth.с
В итоге создается библиотечный файл DLL – ExtMeth .dll
.
Далее следует программа на С#, в которой применяется внешний метод AbsMax()
.
using System;
using System.Runtime.InteropServices;
class ExternMeth {
// Здесь объявляется внешний метод.
[DllImport(«ExtMeth.dll»)]
public extern static int AbsMax(int a, int b);
static void Main() {
// Использовать внешний метод,
int max = AbsMax(-10, -20);
Console.WriteLine(max);
}
}
Обратите внимание на использование атрибута DllImport
в приведенной выше программе. Он уведомляет компилятор о наличии библиотеки DLL, содержащей внешний метод AbsMax()
. В данном случае это файл ExtMeth.dll
, созданный во время компиляции файла с исходным текстом метода AbsMax()
на С. В результате выполнения данной программы на экран, как и ожидалось, выводится значение 20.
Объявление псевдонима внешней сборки
Во втором применении ключевое слово extern
предоставляет псевдоним для внешней сборки, что полезно в тех случаях, когда в состав программы включаются две отдельные сборки с одним и тем же именем элемента. Так, если в сборке test1
содержится класс MyClass
, а в сборке test2
класс с таким же именем, то при обращении к классу по этому имени в одной и той же программе может возникнуть конфликт.
Для разрешения подобного конфликта необходимо создать псевдоним каждой сборки. Это делается в два этапа. На первом этапе нужно указать псевдонимы, используя параметр компилятора /г, как в приведенном ниже примере.
/г:Asm1=test1 /г:Asm2=test2
А на втором этапе необходимо ввести операторы с ключевым словом extern
, в которых делается ссылка на указанные выше псевдонимы. Ниже приведена форма такого оператора для создания псевдонима сборки.
extern alias имя_сборки;
Если продолжить приведенный выше пример, то в программе должны появиться следующие строки кода.
extern alias Asml;
extern alias Asm2;
Теперь оба варианта класса MyClass
будут доступны в программе по соответствующему псевдониму.
Рассмотрим полноценный пример программы, в которой демонстрируется применение внешних псевдонимов. Эта программа состоит из трех файлов. Ниже приведен исходный текст, который следует поместить в первый файл – test1.cs
.
using System;
namespace MyNS {
public class MyClass {
public MyClass() {
Console.WriteLine(«Конструирование из файла MyClassl.dll.»);
}
}
}
Далее следует исходный текст из файла test2.cs.
using System;
namespace MyNS {
public class MyClass {
public MyClass() {
Console.WriteLine(«Конструирование из файла MyClass2.dll.»);
}
}
}
Обратите внимание на то, что в обоих файлах, test1.cs
и test2.cs
, объявляется пространство имен MyNS
и что именно в этом пространстве в обоих файлах определяется класс MyClass
. Следовательно, без псевдонима оба варианта класса MyClass
будут недоступными ни одной из программ.
И наконец, ниже приведен исходный текст из третьего файла test3.cs
, где используются оба варианта класса MyClass
из файлов test1.cs
и test2.cs
. Это становится возможным благодаря операторам с внешними псевдонимами.
// Операторы с внешними псевдонимами должны
// быть указаны в самом начале файла,
extern alias Asml;
extern alias Asm2;
using System;
class Demo {
static void Main() {
Asm1::MyNS.MyClass t = new Asm1::MyNS.MyClass() ;
Asm2::MyNS.MyClass t2 = new Asm2::MyNS.MyClass();
}
}
Сначала следует скомпилировать файлы test1.cs
и test2.cs
в их библиотечные эквиваленты DLL. Для этого достаточно ввести в командной строке следующее.
csc /t:library test1.cs
csc /t:library test2.cs
Затем необходимо скомпилировать файл test3.cs
, указав в командной строке
csc /r:Asm1=test1.dll /r:Asm2=test2.dll test3.cs
Обратите внимание на применение параметра /r
, уведомляющего компилятор о том, что ссылка на метаданные находится в соответствующем файле. В данном случае псевдоним Asm1
связывается с файлом test1.dll
, а псевдоним Asm2
– с файлом test2.dll
.
В самой программе псевдонимы указываются в приведенных ниже операторах с модификатором extern
, которые располагаются в самом начале файла.
extern alias Asm1; extern alias Asm2;
А в методе Main()
псевдонимы используются для разрешения неоднозначности ссылок на класс MyClass
. Обратите внимание на следующее применение псевдонима для обращения к классу MyClass
.
Asm1::MyNS.MyClass
В этой строке кода первым указывается псевдоним, затем оператор разрешения пространства имен, далее имя пространства имен, в котором находится класс с неоднозначным именем, и, наконец, имя самого класса, следующее после оператора-точки. Та же самая общая форма пригодна и для других внешних псевдонимов.
Ниже приведен результат выполнения данной программы.
Конструирование из файла MyClassl.dll.
Конструирование из файла MyClass2.dll.
ЧАСТЬ 2 Библиотека C#
В части II рассматривается библиотека С#. Как пояснялось в части I, используемая в C# библиотека на самом деле является библиотекой классов для среды .NET Framework. Поэтому материал этой части книги имеет отношение не только к языку С#, но и ко всей среде .NET Framework в целом.
Библиотека классов для среды .NET Framework организована по пространствам имен. Для использования отдельной части этой библиотеки, как правило, достаточно импортировать ее пространство имен, указав его с помощью директивы using
в исходном тексте программы. Конечно, ничто не мешает определить имя отдельного элемента библиотеки полностью вместе с его пространством имен, но ведь намного проще импортировать сразу все пространство имен.
Библиотека среды .NET Framework довольно обширна, и поэтому ее полное описание выходит за рамки этой книги. (На самом деле для этого потребовалась бы отдельная и довольно объемистая книга!) Поэтому в части II рассматриваются лишь самые основные элементы данной библиотеки, многие из которых находятся в пространстве имен System
. Кроме того, в этой части описываются классы коллекций, а также вопросы организации многопоточной обработки и сетей.
–
ПРИМЕЧАНИЕ
Классы ввода-вывода подробно рассматривались в главе 14.
–
ГЛАВА 21 Пространство имен System
ГЛАВА 22 Строки и форматирование
ГЛАВА 23 Многопоточное программирование. Часть первая: основы
ГЛАВА 24 Многопоточное программирование. Часть вторая: библиотека TPL
ГЛАВА 25 Коллекции, перечислители и итераторы
ГЛАВА 26 Сетевые средства подключения к Интернету
ГЛАВА 21 Пространство имен SystemВ этой главе речь пойдет о пространстве имен System
. Это пространство имен самого верхнего уровня в библиотеке классов для среды .NET Framework. В нем непосредственно находятся те классы, структуры, интерфейсы, делегаты и перечисления, которые чаще всего применяются в программах на C# или же считаются неотъемлемой частью среды .NET Framework. Таким образом, пространство имен System
составляет ядро рассматриваемой здесь библиотеки классов.
Кроме того, в пространство имен System
входит много вложенных пространств имен, поддерживающих отдельные подсистемы, например System.Net
. Некоторые из этих пространств имен рассматриваются далее в этой книге. А в этой главе речь пойдет только о членах самого пространства имен System
.
Помимо большого количества классов исключений, в пространстве имен содержатся приведенные ниже классы.
ActivationContext
Activator
AppDomain
AppDomainManager
AppDomainSetup
Applicationld
Applicationldentity
Array
AssemblyLoadEventArgs
Attribute
AttributeUsageAttribute
BitConverter
Buffer
CharEnumerator
CLSCompliantAttribute
Console
ConsoleCancelEventArgs
ContextBoundObject
ContextStaticAttribute
Convert
DBNull
Delegate
Enum
Environment
EventArgs
Exception
FileStyleUriParser
FlagsAttribute
FtpStylellri Parser
GC
GenericUriParser
GopherStylellri Parser
HttpStyleUri Parser
Lazy
Lazy
LdapStyleUriParser
LoaderOptimizationAttribute
LocalDataStoreSlot
MarshalByRefObject
Math
MTAThreadAttribute
MulticastDelegate
NetPipeStylellriParser
NetTcpStylellriParser
NewsStyleUriParser
NonSerializedAttribute
Nullable
Object
ObsoleteAttribute
OperatingSystem
ParamArrayAttribute
Random
ResolveEventArgs
SerializableAttribute
STAThreadAttribute
String
StringComparer
ThreadStaticAttribute
TimeZone
TimeZonelnfo
TimeZonelnfo.AdjustmentRule
Tuple
Tuple<...> (различные формы)
Type
Unhandled Exception EventArgs
Uri
UriBuilder
Uri Parser
UriTemplate
UriTemplateEquivalenceComparer
UriTemplateMatch
UriTemplateTable
UriTypeConverter
ValueType
Version
WeakReference
Ниже приведены структуры, определенные в пространстве имен System
.
Arglterator
ArraySegment
Boolean
Byte
Char
ConsoleKeylnfo
DateTime
DateTimeOffset
Decimal
Double
Guid
Int16
Int32
Int64
IntPtr
ModuleHandle
Nullable
RuntimeArgumentHandle
RuntimeFieldHandle
RuntimeMethodHandle
RuntimeTypeHandle
Sbyte
Single
TimeSpan
TimeZonelnfo.TransitionTime
Typed Reference
Uint16
Ulnt32
Ulnt64
UIntPtr
Void
В пространстве имен System определены приведенные ниже интерфейсы
_AppDomain
lappDomainSetup
lAsyncResult
ICIoneable
IComparable
IComparable
IConvertible
ICustomFormatter
IDisposable
IEquatable
IFormatProvider
IFormattable
IObservable
IObserver
IServiceProvider
Ниже приведены делегаты, определенные в пространстве имен System
.
Action
Action<...> (различные формы)
AppDomainlnitializer
AssemblyLoadEventHandler
AsyncCallback
Comparison
ConsoleCancelEventHandler
Converter
CrossAppDoma in Delegate
EventHandler
EventHandler
Func<...> (различные формы)
Predicate
ResolveEventHandler
UnhandledExceptionEventHandler
В пространстве имен System
определены приведенные ниже перечисления.
ActivationContext.contextForrr
AppDomainManagerlnitializationOptions AttributeTargets
Base64Formatting0ptions
ConsoleColor
ConsoleKey
ConsoleModifiers
ConsoleSpecialKey
DateTimeKind
DayOfWeek
Environment.SpecialFolder
Environment.SpecialFolderOption
EnvironmentVariableTarget
GCCol lection Mode
GCNotificationStatus
GenericUriParserOptions
LoaderOptimization
MidpointRounding
PlatformID
StringComparison
StringSplitOptions
TypeCode
UriComponents
UriFormat
UriHostNameType
UrildnScope
UriKind
UriPartial
Как следует из приведенных выше таблиц, пространство имен System
довольно обширно, поэтому в одной главе невозможно рассмотреть подробно все его составляющие. К тому же, некоторые члены пространства имен System
, в том числе Nullable
, Type
, Exception
и Attribute
, уже рассматривались в части I или будут представлены в последующих главах части II. И наконец, класс System.String
, в котором определяется тип string
для символьных строк в С#, обсуждается вместе с вопросами форматирования в главе 22. В силу этих причин в настоящей главе рассматриваются только те члены данного пространства имен, которые чаще всего применяются в программировании на C# и не поясняются полностью в остальных главах книги.