355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Герберт Шилдт » C# 4.0: полное руководство » Текст книги (страница 56)
C# 4.0: полное руководство
  • Текст добавлен: 6 апреля 2017, 04:00

Текст книги "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.


Члены пространства имен 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# и не поясняются полностью в остальных главах книги.


    Ваша оценка произведения:

Популярные книги за неделю