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

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

Текст книги "C# 4.0: полное руководство"


Автор книги: Герберт Шилдт



сообщить о нарушении

Текущая страница: 18 (всего у книги 83 страниц)

Инициализаторы объектов

Инициализаторы объектов предоставляют еще один способ создания объекта и инициализации его полей и свойств. (Подробнее о свойствах речь пойдет в главе 10.) Если используются инициализаторы объектов, то вместо обычного вызова конструктора класса указываются имена полей или свойств, инициализируемых первоначально задаваемым значением. Следовательно, синтаксис инициализатора объекта предоставляет альтернативу явному вызову конструктора класса. Синтаксис инициализатора объекта используется главным образом при создании анонимных типов в LINQ-выражениях. (Подробнее об анонимных типах и LINQ-выражениях – в главе 19.) Но поскольку инициализаторы объектов можно, а иногда и должно использовать в именованном классе, то ниже представлены основные положения об инициализации объектов.

Обратимся сначала к простому примеру.

// Простой пример, демонстрирующий применение инициализаторов объектов.

using System;

class MyClass {

  public int Count;

  public string Str;

}

class ObjInitDemo {

  static void Main() {

    // Сконструировать объект типа MyClass, используя инициализаторы объектов.

    MyClass obj = new MyClass { Count = 100, Str = «Тестирование» };

    Console.WriteLine(obj.Count + " " + obj.Str);

  }

}

Выполнение этого кода дает следующий результат.

100 Тестирование

Как показывает результат выполнения приведенного выше кода, переменная экземпляра obj.Count инициализирована значением 100, а переменная экземпляра obj.Str – символьной строкой «Тестирование». Но обратите внимание на то, что в классе MyClass отсутствуют явно определяемые конструкторы и не используется обычный синтаксис конструкторов. Вместо этого объект obj класса MyClass создается с помощью следующей строки кода.

MyClass obj = new MyClass { Count = 100, Str = «Тестирование» };

В этой строке кода имена полей указываются явно вместе с их первоначальными значениями. Это приводит к тому, что сначала конструируется экземпляр объекта типа MyClass (с помощью неявно вызываемого по умолчанию конструктора), а затем задаются первоначальные значения переменных Count и Str данного экземпляра.

Следует особо подчеркнуть, что порядок указания инициализаторов особого значения не имеет. Например, объект obj можно было бы инициализировать и так, как показано ниже.

MyClass obj = new MyClass { Str = «Тестирование», Count = 100 };

В этой строке кода инициализация переменной экземпляра Str предшествует инициализации переменной экземпляра Count, а в приведенном выше коде все происходило наоборот. Но в любом случае результат получается одинаковым.

Ниже приведена общая форма синтаксиса инициализации объектов:

new имя_класса {имя = выражение, имя = выражение, . . . }

где имя обозначает имя поля или свойства, т.е. доступного члена класса, на который указывает имя_класса. А выражение обозначает инициализирующее выражение, тип которого, конечно, должен соответствовать типу поля или свойства.

Инициализаторы объектов обычно не используются в именованных классах, как, например, в представленном выше классе MyClass, хотя это вполне допустимо. Вообще, при обращении с именованными классами используется синтаксис вызова обычного конструктора. И, как упоминалось выше, инициализаторы объектов применяются в основном в анонимных типах, формируемых в LINQ-выражениях.


Необязательные аргументы

В версии C# 4.0 внедрено новое средство, повышающее удобство указания аргументов при вызове метода. Это средство называется необязательными аргументами и позволяет определить используемое по умолчанию значение для параметра метода.

Данное значение будет использоваться по умолчанию в том случае, если для параметра не указан соответствующий аргумент при вызове метода. Следовательно, указывать аргумент для такого параметра не обязательно. Необязательные аргументы позволяют упростить вызов методов, где к некоторым параметрам применяются аргументы, выбираемые по умолчанию. Их можно также использовать в качестве "сокращенной" формы перегрузки методов.

Применение необязательного аргумента разрешается при создании необязательного параметра. Для этого достаточно указать используемое по умолчанию значение параметра с помощью синтаксиса, аналогичного инициализации переменной. Используемое по умолчанию значение должно быть константным выражением. В качестве примера рассмотрим следующее определение метода.

static void OptArgMeth(int alpha, int beta=10, int gamma = 20)    {

В этой строке кода объявляются два необязательных параметра: beta и gamma, причем параметру beta по умолчанию присваивается значение 10, а параметру gamma – значение 20. Эти значения используются по умолчанию, если для данных параметров не указываются аргументы при вызове метода. Следует также иметь в виду, что параметр alpha не является необязательным. Напротив, это обычный параметр, для которого всегда нужно указывать аргумент.

Принимая во внимание приведенное выше объявление метода OptArgMeth(), последний можно вызвать следующими способами.

// Передать все аргументы явным образом.

OptArgMeth(1, 2, 3);

// Сделать аргумент gamma необязательным.

OptArgMeth(1, 2);

// Сделать оба аргумента beta и gamma необязательными.

OptArgMeth(1);

При первом вызове параметру alpha передается значение 1, параметру beta – значение 2, а параметру gamma – значение 3. Таким образом, все три аргумента задаются явным образом, а значения, устанавливаемые по умолчанию, не используются. При втором вызове параметру alpha передается значение 1, а параметру beta – значение 2, но параметру gamma присваивается устанавливаемое по умолчанию значение 20. И наконец, при третьем вызове упомянутого выше метода параметру alpha передается значение 1, а параметрам beta и gamma присваиваются устанавливаемые по умолчанию значения. Следует, однако, иметь в виду, что параметр beta не получит устанавливаемое по умолчанию значение, если то же самое не произойдет с параметром gamma. Если первый аргумент устанавливается по умолчанию, то и все остальные аргументы должны быть установлены по умолчанию.

Весь описанный выше процесс демонстрируется в приведенном ниже примере программы.

// Продемонстрировать необязательные аргументы.

using System;

class OptionArgDemo {

  static void OptArgMeth(int alpha, int beta=10, int gamma = 20) {

     Console.WriteLine("Это аргументы alpha, beta и gamma: " 

            + alpha + " " + beta + " " + gamma);

  }

  static void Main() {

    // Передать все аргументы явным образом.

    OptArgMeth(1, 2, 3);    i

    // Сделать аргумент gamma необязательным.

    OptArgMeth(1, 2);

    // Сделать оба аргумента beta и gamma необязательными.

    OptArgMeth(1);

  }

}

Результат выполнения данной программы лишь подтверждает применение используемых по умолчанию аргументов.

Это аргументы alpha, beta и gamma: 1 2 3

Это аргументы alpha, beta и gamma: 1 2 20

Это аргументы alpha, beta и gamma: 1 10 20

Как следует    из приведенного выше результата, если аргумент не указан, то используется его значение, устанавливаемое по умолчанию.

Следует иметь в виду, что все необязательные аргументы должны непременно указываться справа от обязательных. Например, следующее объявление оказывается недействительным.

int Sample(string name = «пользователь», int userid) { // Ошибка!

Для исправления ошибки в этом объявлении необходимо указать аргумент userid до аргумента name. Раз уж вы начали объявлять необязательные аргументы, то указывать после них обязательные аргументы нельзя. Например, следующее объявление также оказывается неверным.

int Sample(int accountld, string name = «пользователь», int userid) { //.Ошибка!

Аргумент name объявляется как необязательный, и поэтому аргумент userid следует указать до аргумента name (или же сделать его также необязательным).

Помимо методов, необязательные аргументы можно применять в конструкторах, индексаторах и делегатах. (Об индексаторах и делегатах речь пойдет далее в этой книге.)

Преимущество необязательных аргументов заключается, в частности, в том, что они упрощают программирующему обращение со сложными вызовами методов и конструкторов. Ведь нередко в методе приходится задавать больше параметров, чем обычно требуется. И в подобных случаях некоторые из этих параметров могут быть сделаны необязательными благодаря аккуратному применению необязательных аргументов. Это означает, что передавать нужно лишь те аргументы, которые важны в данном конкретном случае, а не все аргументы, которые в противном случае должны быть обязательными. Такой подход позволяет рационализировать метод и упростить программирующему обращение с ним.


Необязательные аргументы и перегрузка методов

В некоторых случаях необязательные аргументы могут стать альтернативой перегрузке методов. Для того чтобы стало понятнее, почему это возможно, обратимся еще раз к примеру метода OptArgMeth(). До появления в C# необязательных аргументов нам пришлось бы создать три разных варианта метода OptArgMeth(), чтобы добиться таких же функциональных возможностей, как и у рассмотренного выше варианта этого метода. Все эти варианты пришлось бы объявить следующим образом.

static void    OptArgMeth(int alpha)

static void    OptArgMeth(int alpha, int beta)

static void    OptArgMeth(int alpha, int beta, int gamma)

Эти перегружаемые варианты метода OptArgMeth() позволяют вызывать его с одним, двумя или тремя аргументами. (Если значения параметров beta и gamma не передаются, то они предоставляются в теле перегружаемых вариантов данного метода.) Безусловно, в такой реализации функциональных возможностей метода OptArgMeth() с помощью перегрузки нет ничего дурного. Но в данном случае целесообразнее все же воспользоваться необязательными аргументами, хотя такой подход не всегда оказывается более совершенным, чем перегрузка метода.


Необязательные аргументы и неоднозначность

При использовании необязательных аргументов может возникнуть такое затруднение, как неоднозначность. Нечто подобное может произойти при перегрузке метода с необязательными параметрами. В некоторых случаях компилятор может оказаться не в состоянии определить, какой именно вариант метода следует вызывать, когда необязательные аргументы не заданы. В качестве примера рассмотрим два следующих варианта метода OptArgMeth().

static void OptArgMeth(int alpha, int beta=10, int gamma = 20) {

  Console.WriteLine("Это аргументы alpha, beta и gamma: " 

         + alpha + " " + beta + " " + gamma);

}

static void OptArgMeth(int alpha, double beta=10.0, double gamma =20.0) {

  Console.WriteLine("Это аргументы alpha, beta и gamma: " 

         + alpha + " " + beta + " " + gamma);

}

Обратите внимание на то, что единственное отличие в обоих вариантах рассматриваемого здесь метода состоит в типах параметров beta и gamma, которые оказываются необязательными. В первом варианте оба параметра относятся к типу int, а во втором – к типу double. С учетом этих вариантов перегрузки метода OptArgMeth() следующий его вызов приводит к неоднозначности.

OptArgMeth(1); // Ошибка из-за неоднозначности!

Этот вызов приводит к неоднозначности потому, что компилятору неизвестно, какой именно вариант данного метода использовать: тот, где параметры beta и gamma имеют тип int, или же тот, где они имеют тип double. Но самое главное, что конкретный вызов метода OptArgMeth() может привести к неоднозначности, даже если она и не присуща его перегрузке.

В связи с тем что перегрузка методов, допускающих применение необязательных аргументов, может привести к неоднозначности, очень важно принимать во внимание последствия такой перегрузки. В некоторых случаях, возможно, придется отказаться от применения необязательных аргументов, чтобы исключить неоднозначность и тем самым предотвратить использование метода непреднамеренным образом.


Практический пример использования необязательных аргументов

Для того чтобы показать на практике, насколько необязательные аргументы упрощают вызовы некоторых типов методов, рассмотрим следующий пример программы. В этой программе объявляется метод Display(), выводящий на экран символьную строку полностью или частично.

// Использовать необязательный аргумент, чтобы упростить вызов метода.

using System;

class UseOptArgs {

// Вывести на экран символьную строку полностью или частично.

  static void Display(string str, int start = 0, int stop = -1) {

    if(stop < 0)

      stop = str.Length;

    // Проверить условие выхода за заданные пределы.

    if(stop > str.Length | start > stop | start < 0) return;

    for (int i=start; i < stop; i++)

      Console.Write(str[i] ) ;

    Console.WriteLine();

  }

  static void Main() {

    Display(«это простой тест»);

    Display(«это простой тест», 12);

    Display(«3TO простой тест», 4, 14);

  }

}

Выполнение этой программы дает следующий результат.

это простой тест

тест

простой те

Внимательно проанализируем метод Display(). Выводимая на экран символьная строка передается в первом аргументе данного метода. Это обязательный аргумент, а два других аргумента – необязательные. Они задают начальный и конечный индексы для вывода части символьной строки. Если параметру stop не передается значение, то по умолчанию он принимает значение -1, указывающее на то, что конечной точкой вывода служит конец символьной строки. Если же параметру start не передается значение, то по умолчанию он принимает значение 0. Следовательно, в отсутствие одного из необязательных аргументов символьная строка выводится на экран полностью. В противном случае она выводится на экран частично. Это означает, что если вызвать метод Display() с одним аргументом (т.е. с выводимой строкой), то символьная строка будет выведена на экран полностью. Если же вызвать метод Display() с двумя аргументами, то на экран будут выведены символы, начиная с позиции, определяемой аргументом start, и до самого конца строки. А если вызвать метод Display() с тремя аргументами, то на экран будут выведены символы из строки, начиная с позиции, определяемой аргументом start, и заканчивая позицией, определяемой аргументом stop.

Несмотря на всю простоту данного примера, он, тем не менее, демонстрирует значительное преимущество, которое дают необязательные аргументы. Это преимущество заключается в том, что при вызове метода можно указывать только те аргументы, которые требуются. А передавать явным образом устанавливаемые по умолчанию значения не нужно.

Прежде чем переходить к следующей теме, остановимся на следующем важном моменте. Необязательные аргументы оказываются весьма эффективным средством лишь в том случае, если они используются правильно. Они предназначены для того, чтобы метод выполнял свои функции эффективно, а пользоваться им можно было бы просто и удобно. В этом отношении устанавливаемые по умолчанию значения всех аргументов должны упрощать обычное применение метода. В противном случае необязательные аргументы способны нарушить структуру кода и ввести в заблуждение тех, кто им пользуется. И наконец, устанавливаемое по умолчанию значение необязательного параметра не должно наносить никакого вреда. Иными словами, неумышленное использование необязательного аргумента не должно приводить к необратимым, отрицательным последствиям. Так, если забыть указать аргумент при вызове метода, то это не должно привести к удалению важного файла данных!


Именованные аргументы

Еще одним средством, связанным с передачей аргументов методу, является именованный аргумент. Именованные аргументы были внедрены в версии C# 4.0. Как вам должно быть уже известно, при передаче аргументов методу порядок их следования, как правило, должен совпадать с тем порядком, в котором параметры определены в самом методе. Иными словами, значение аргумента присваивается параметру по его позиции в списке аргументов. Данное ограничение призваны преодолеть именованные аргументы. Именованный аргумент позволяет указать имя того параметра, которому присваивается его значение. И в этом случае порядок следования аргументов уже не имеет никакого значения. Таким образом, именованные аргументы в какой-то степени похожи на упоминавшиеся ранее инициализаторы объектов, хотя и отличаются от них своим синтаксисом.

Для указания аргумента по имени служит следующая форма синтаксиса.

имя_параметра : значение

Здесь имя_параметра обозначает имя того параметра, которому передается значение. Разумеется, имя_параметра должно обозначать имя действительного параметра для вызываемого метода.

Ниже приведен простой пример, демонстрирующий применение именованных аргументов. В этом примере создается метод IsFactor(), возвращающий логическое значение true, если первый его параметр нацело делится на второй параметр.

// Применить именованные аргументы,

using System;

class NamedArgsDemo {

// Выяснить, делится ли одно значение нацело на другое.

  static bool IsFactor(int val, int divisor) {

    if((val % divisor) == 0) return true;

    return false;

  }

  static void Main() {

    // Ниже демонстрируются разные способы вызова метода IsFactor().

    // Вызов с использованием позиционных аргументов.

    if(IsFactor(10, 2))

      Console.WriteLine(«2 – множитель 10.»);

    // Вызов с использованием именованных аргументов.

    if(IsFactor(val: 10, divisor: 2))

      Console.WriteLine(«2 – множитель 10.»);

    // Для именованного аргумента порядок указания не имеет значения.

    if(IsFactor(divisor: 2, val: 10))

      Console.WriteLine(«2 – множитель 10.»);

    // Применить как позиционный, так и именованный аргумент.

    if(IsFactor(10, divisor: 2))

      Console.WriteLine(«2 – множитель 10.»);

  }

}

Выполнение этого кода дает следующий результат.

2 – множитель 10.

2 – множитель 10.

2 – множитель 10.

2 – множитель 10.

Как видите, при каждом вызове метода IsFactor() получается один и тот же результат.

Помимо демонстрации именованного аргумента в действии, приведенный выше пример кода иллюстрирует две важные особенности именованных аргументов. Во-первых, порядок следования аргументов не имеет никакого значения. Например, два следующих вызова метода IsFactor() совершенно равнозначны.

IsFactor(val :10, divisor: 2)

IsFactor(divisor: 2, val: 10)

Независимость от порядка следования является главным преимуществом именованных аргументов. Это означает, что запоминать (или даже знать) порядок следования параметров в вызываемом методе совсем не обязательно. Для работы с СОМ-интерфейсами это может быть очень удобно. И во-вторых, позиционные аргументы можно указывать вместе с именованными в одном и том же вызове, как показано в следующем примере.

IsFactor(10, divisor: 2)

Следует, однако, иметь в виду, что при совместном использовании именованных и позиционных аргументов все позиционные аргументы должны быть указаны перед любыми именованными аргументами.

Именованные аргументы можно также применять вместе с необязательными аргументами. Покажем это на примере вызова метода Display(), рассматривавшегося в предыдущем разделе.

// Указать все аргументы по имени.

Display(stop: 10, str: «это простой тест», start: 0);

// Сделать аргумент start устанавливаемым по умолчанию.

Display(stop: 10, str: «это простой тест») ;

// Указать строку по позиции, аргумент stop – по имени by name,

// тогда как аргумент start – устанавливаемым по умолчанию

Display(«это простой тест», stop: 10);

Вообще говоря, комбинация именованных и необязательных аргументов позволяет упростить вызовы сложных методов со многими параметрами.

Синтаксис именованных аргументов более многословен, чем у обычных позиционных аргументов, и поэтому для вызова методов чаще всего применяются позиционные аргументы. Но в тех случаях, когда это уместно, именованные аргументы могут быть использованы довольно эффективно.

ПРИМЕЧАНИЕ

Помимо методов, именованные и необязательные аргументы могут применяться в конструкторах, индексаторах и делегатах. (06 индексаторах и делегатах речь пойдет далее в этой книге.)


Метод Main()

В представленных до сих пор примерах программ использовалась одна форма метода Main(). Но у него имеется также целый ряд перегружаемых форм. Одни из них могут служить для возврата значений, другие – для получения аргументов. В этом разделе рассматриваются и те и другие формы.


Возврат значений из метода Main()

По завершении программы имеется возможность возвратить конкретное значение из метода Main() вызывающему процессу (зачастую операционной системе). Для этой цели служит следующая форма метода Main().

static int Main()

Обратите внимание на то, что в этой форме метода Main() объявляется возвращаемый тип int вместо типа void.

Как правило, значение, возвращаемое методом Main(), указывает на нормальное завершение программы или на аварийное ее завершение из-за сложившихся ненормальных условий выполнения. Условно нулевое возвращаемое значение обычно указывает на нормальное завершение программы, а все остальные значения обозначают тип возникшей ошибки.


Передача аргументов методу Main()

Многие программы принимают так называемые аргументы командной строки, т.е. информацию, которая указывается в командной строке непосредственно после имени программы при ее запуске на выполнение. В программах на C# такие аргументы передаются затем методу Main(). Для получения аргументов служит одна из приведенных ниже форм метода Main().

static void Main(string[ ] args)

static int Main(string[ ] args)

В первой форме метод Main() возвращает значение типа void, а во второй – целое значение, как пояснялось выше. Но в обеих формах аргументы командной строки сохраняются в виде символьных строк в массиве типа string, который передается методу Main(). Длина этого массива (args) должна быть равна числу аргументов командной строки, которое может быть и нулевым.

В качестве примера ниже приведена программа, выводящая все аргументы командной строки, вместе с которыми она вызывается.

// Вывести все аргументы командной строки.

using System;

class CLDemo {

  static void Main(string[] args) {

    Console.WriteLine("Командная строка содержит " + args.Length +

            « аргумента.»);

    Console.WriteLine("Вот они: ");

    for(int i=0; i < args.Length; i++)

      Console.WriteLine(args[i]);

  }

}

Если программа CLDemo запускается из командной строки следующим образом:

CLDemo один два три

то ее выполнение дает такой результат.

Командная строка содержит 3 аргумента.

Вот они:

один

два

три

Для того чтобы стало понятнее, каким образом используются аргументы командной строки, рассмотрим еще один пример программы, в которой применяется простой подстановочный шифр для шифровки или расшифровки сообщений. Шифруемое или расшифровываемое сообщение указывается в командной строке. Применяемый шифр действует довольно просто. Для шифровки слова значение каждой его буквы инкрементируется на 1. Следовательно, Буква "А" становится буквой "Б" и т.д. А для расшифровки слова значение каждой его буквы декрементируется на 1. Разумеется, такой шифр не имеет никакой практической ценности, поскольку его нетрудно разгадать. Тем не менее он может стать приятным развлечением для детей.

// Зашифровать и расшифровать сообщение, используя

// простой подстановочный шифр.

using System;

class Cipher {

  public static int Main(string[] args) {

    // Проверить наличие аргументов.

    if (args.Length < 2) {

      Console.WriteLine("ПРИМЕНЕНИЕ: " +

            "слово1: <зашифровать>/<расшифровать> " +

            «[слово2... словоN]»);

      return 1; // возвратить код неудачного завершения программы

    }

    // Если аргументы присутствуют, то первым аргументом должно быть

    // слово <зашифровать> или же слово <расшифровать>.

    if (args[0] != «зашифровать» & args[0] != «расшифровать») {

      Console.WriteLine("Первым аргументом должно быть слово "

          + «<зашифровать> или <расшифровать>.»);

      return 1; // возвратить код неудачного завершения программы

    }

    // Encode or decode message.

    for (int n = 1; n < args.Length; n++) {

      for (int i = 0; i < args[n].Length; i++) {

        if (args[0] == «зашифровать»)

          Console.Write((char)(args[n][i] + 1));

        else

          Console.Write((char)(args[n][i] – 1));

      }

      Console.Write(" ");

    }

    Console.WriteLine();

    return 0;

  }

}

Для того чтобы воспользоваться этой программой, укажите в командной строке имя программы, затем командное слово «зашифровать» или «расшифровать» и далее сообщение, которое требуется зашифровать или расшифровать. Ниже приведены два примера выполнения данной программы, при условии, что она называется Cipher.

C:Cipher зашифровать один два

пейо егб

C:Cipher расшифровать пейо егб

один два

Данная программа отличается двумя интересными свойствами. Во-первых, обратите внимание на то, как в ней проверяется наличие аргументов командной строки перед тем, как продолжить выполнение. Это очень важное свойство, которое можно обобщить. Если в программе принимается во внимание наличие одного или более аргументов командной строки, то в ней должна быть непременно организована проверка факта передачи ей предполагаемых аргументов, иначе программа будет работать неправильно. Кроме того, в программе должна быть организована проверка самих аргументов перед тем, как продолжить выполнение. Так, в рассматриваемой здесь программе проверяется наличие командного слова «зашифровать» или «расшифровать» в качестве первого аргумента командной строки.

И во-вторых, обратите внимание на то, как программа возвращает код своего завершения. Если предполагаемые аргументы командной строки отсутствуют или указаны неправильно, программа возвращает код 1, указывающий на ее аварийное завершение. В противном случае возвращается код 0, когда программа завершается нормально.


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

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