Инструкции
Большинство видов инструкций в Java и C# являются общими и заимствованы из языка C. В обоих языках есть понятие блока — набора инструкций, заключенного в фигурные скобки.
- Пустая инструкция ; допускается в обоих языках.
- Декларации локальных переменных устроены совершенно одинаково — указывается тип переменной, затем ее идентификатор, а затем, возможно, инициализация.
Инициализировать переменную можно каким-то значением ее типа. Использование неинициализированных переменных во многих случаях определяется компилятором и считается ошибкой (но не всегда). Однако даже при отсутствии инициализации переменной, ей все равно будет присвоено значение по умолчанию для данного типа.
Массивы могут быть инициализированы с помощью специальных выражений, перечисляющих значения элементов массива, например
int[][] array = new int[][]{{0, 1}, {2, 3, 4}}; Ag
- Инструкция может быть помечена с помощью метки, которая стоит перед самой инструкцией и отделяется от нее с помощью двоеточия.
- Инструкция может быть построена добавлением точки с запятой в конец выражения определенного вида. Такое выражение должно быть одним из следующих:
- присваиванием;
- выражением, в котором последним оператором было уменьшение или увеличение на единицу (++, --), все равно, префиксное или постфиксное;
- вызовом метода в объекте или классе (в C# — еще и вызовом делегата);
- созданием нового объекта.
- Условная инструкция имеет вид
if(expression) statement Ag
или
if(expression) statement else statement1 Ag
где expression — выражение логического типа (или приводящегося к логическому), а statement и statement1 — инструкции.
- Инструкция выбора имеет вид
switch(expression) { … } Ag
Внутри ее блока различные варианты действий для различных значений выражения expression описываются с помощью списков инструкций, помеченных либо меткой case с возможным значением выражения, либо меткой default. Группа инструкций, помеченная default, выполняется, если значение выражения выбора не совпало ни с одним из значений, указанных в метках case.
Один набор инструкций может быть помечен несколькими метками. Наборы инструкций могут отделяться друг от друга инструкциями break;
Тип expression может быть целочисленным или приводящимся к нему, либо перечислимым типом. В C# допускается использование для выбора выражений типа string.
Значения, которые используются в метках case, должны быть константными выражениями.
В Java группа инструкций для одного значения может оканчиваться инструкцией break, а может и не оканчиваться. Во втором случае после ее выполнения управление переходит на следующую группу инструкций.
В C# группа инструкций для одного значения (включая и группу, помеченную default) всегда должна оканчиваться либо break, либо goto default, либо goto case value с каким-то из значений, указанных в рамках той же инструкции выбора.
public class A { public static void main(String[] args) { if(args.length > 0) { int n = Integer.parseInt(args[0]); switch(n) { case 0: System.out.println("n = 0");
case 1: System.out.println ("n = 0 or n = 1"); break; case 2:case 3: System.out.println ("n = 2 or n = 3"); break; default: System.out.println ("n is out of [0..3]");
} } else System.out.println("No arguments"); } }using System;
public class A { public static void Main(string[] args) { if(args.Length > 0) { int n = Int32.Parse(args[0]); switch(n) { case 0: Console.WriteLine("n = 0"); goto case 1; case 1: Console.WriteLine ("n = 0 or n = 1"); break; case 2:case 3: Console.WriteLine ("n = 2 or n = 3"); break; default: Console.WriteLine ("n is out of [0..3]"); break; } } else Console.WriteLine("No arguments"); } } - Циклы while и do в обоих языках устроены одинаково:
while(expression) statement Ag do statement while(expression); Ag
Здесь expression — логическое выражение, условие цикла, statement — тело цикла. Правила выполнения этих циклов фактически заимствованы из языка C. Первый на каждой итерации проверяет условие и, если оно выполнено, выполняет свое тело, а если нет — передает управление дальше.
Второй цикл сначала выполняет свое тело, а потом проверяет условие. - Цикл for в обоих языках заимствован из языка C.
for(A; B; C) statement Ag
выполняется практически как
A; while(B) { statement C; } Ag
Любой из элементов A, B, C может отсутствовать, B должно быть выражением логического типа (при отсутствии оно заменяется на true), A и С должны быть наборами выражений (A может включать и декларации переменных), разделенных запятыми.
Помимо обычного for, в обоих языках имеется специальная конструкция для цикла, перебирающего элементы коллекции.
<
В Java синтаксис цикла перебора элементов коллекции такой:
for ( finalopt type id : expression ) statement
При этом выражение expression должно иметь тип java.lang.Iterable или тип массива.
В первом случае такой цикл эквивалентен следующему (T далее обозначает тип результат метода iterator() у expression, v — нигде не используемое имя).
for(T v = expression.iterator(); v.hasNext(); ) { finalopt type id = v.next(); statement }
Во втором случае, когда expression — массив типа T[], эта конструкция эквивалентна следующей (a, i — нигде не используемые имена)
T[] a = expression; for(int i = 0; i < a.length; i++) { finalopt type id = v.next(); statement }
В C# синтаксис цикла перебора элементов коллекции такой:
foreach ( type id in expression ) statement
Выражение expression должно быть массивом, или иметь тип System.Collections.IEnumerable или System.Collections.Generic.IEnumerable <T>, или же его тип должен иметь метод GetEnumerator(), результат которого, в свою очередь, должен иметь свойство Current и метод MoveNext().
Тип результата метода GetEnumerator() во всех случаях, кроме массива, называется типом итератора (enumerator type). Тип свойства Current, которое имеется у типа итератора, должен совпадать с type.
Пусть тип итератора E, а e — неиспользуемое имя. Тогда приведенная конструкция, с точностью до некоторых деталей, эквивалентна следующей.
E e = expression.GetEnumerator(); while(e.MoveNext()) { type id = (type)e.Current; statement }
Опущенные детали касаются освобождения ресурсов, используемых итератором (см. далее описание инструкции using).
Пример использования перебора элементов коллекции:
Пример использования перебора элементов коллекции:
public class A { public static void main(String[] args) { int i = 1; for(String s : args) System.out.println((i++) + "-th argument is " + s); } }
using System;
public class A { public static void Main(string[] args) { int i = 1; foreach (string s in args) Console.WriteLine((i++) + "-th argument is " + s); } }
- Инструкции прерывания break и continue также заимствованы из C.
Инструкция break прерывает выполнение самого маленького содержащего ее цикла и передает управление первой инструкции после него. Инструкция continue прерывает выполнение текущей итерации и переходит к следующей, если она имеется (т.е. условие цикла выполнено в сложившейся ситуации), иначе тоже выводит цикл.
При выходе с помощью break или continue за пределы блока try (см. ниже) или блока catch, у которых имеется соответствующий блок finally, сначала выполняется содержимое этого блока finally.
В Java инструкция break используется для прерывания выполнения не только циклов, но и обычных блоков (наборов инструкций, заключенных в фигурные скобки).
Более того, после break (или continue) может стоять метка. Тогда прерывается выполнение того блока/цикла (или же начинается новая итерация того цикла), который помечен этой меткой. Этот блок (или цикл) должен содержать такую инструкцию внутри себя. - Инструкция возврата управления return используется для возврата управления из операции (метода, оператора, метода доступа к свойству и пр., см. далее). Если операция должна вернуть значение некоторого типа, после return должно стоять выражение этого же типа.
- Инструкция создания исключительной ситуации throw используется для выброса исключительной ситуации. При этом после throw должно идти выражение, имеющее тип исключения.
Исключение (exception) представляет собой объект, содержащий информацию о какой-то особой (исключительной) ситуации, в которой операция не может вернуть обычный результат. Вместо обычного результата из нее возвращается объект-исключение — при этом говорят, что исключение было выброшено из операции. Механизм этого возвращения несколько отличается от механизма возвращения обычного результата, и обработка исключений оформляется иначе (см. следующий вид инструкций), чем обработка обычных результатов работы операции. - Исключения в обоих языках относятся к особым типам — классам исключений.
Только объекты таких классов могут быть выброшены в качестве исключений. Классами исключений являются все наследники классов java.lang.Throwable в Java и System.Exception в C#. - Объекты-исключения содержат, как минимум, следующую информацию.
- Сообщение о возникшей ситуации (его должен определить автор кода операции, выбрасывающей это исключение).
В Java это сообщение можно получить с помощью метода String getMessage(), а в C# — с помощью свойства string Message. - Иногда возникают цепочки "наведенных" исключений, если обработка одного вызывает выброс другого. Каждый объект-исключение содержит ссылку на другое исключение, непосредственно вызвавшее это. Если данное исключение не вызвано никаким другим, эта ссылка равна null.
В Java эту ссылку можно получить с помощью метода Throwable getCause(), а в C# — с помощью свойства System.Exception.InnerException. - Для описания ситуации, в которой возникло исключение, используется состояние стека исполнения программы — список методов, которые вызывали друг друга перед этим, и указание на место в коде каждого такого метода. Это место обозначает место вызова следующего метода по стеку или, если это самый последний метод, то место, где и возникло исключение. Обычно указывается номер строки, но иногда он недоступен, если соответствующий метод присутствует в системе только в скомпилированном виде или является внешним для Java-машины.
Информация о состоянии стека на момент возникновения исключения, как и его сообщение, автоматически выводится в поток сообщений об ошибках, если это исключение остается необработанным в программе.
В Java состояние стека для данного исключения можно получить с помощью метода StackTraceElement[] getStackTrace(), возвращающего массив элементов стека. Каждый такой элемент несет информацию о файле (String getFileName()), классе (String getClassName()) и методе (String getMethodName()), а также о номере строки (int getLineNumber()).
В C# можно сразу получить полное описание состояния стека в виде одной строки с помощью свойства string StackTrace.
- Сообщение о возникшей ситуации (его должен определить автор кода операции, выбрасывающей это исключение).
- Блок обработки исключительных ситуаций выглядит так:
try { statements } Ag catch ( type_1 e_1 ) { statements_1 } Ag … Ag catch ( type_n e_n ) { statements_n } Ag finally { statements_f } Ag
Если во время выполнения одной из инструкций в блоке, следующем за try, возникает исключение, управление передается на первый блок catch, обрабатывающий исключения такого же или более широкого типа. Если подходящих блоков catch нет, выполняется блок finally и исключение выбрасывается дальше.
Блок finally выполняется всегда — сразу после блока try, если исключения не возникло, или сразу после обрабатывавшего исключение блока catch, даже если он выбросил новое исключение.
В этой конструкции могут отсутствовать блоки catch или блок finally, но не то и другое одновременно. В C# разрешается опускать имя объекта-исключения в catch, если он не используется при обработке соответствующей исключительной ситуации.
public class A { public static void main(String[] args) { try { if(args.length > 0) System.out.println ("Some arguments are specified"); else throw new IllegalArgumentException ("No arguments specified"); } catch(RuntimeException e) { System.out.println ("Exception caught"); System.out.println ("Exception type is " + e.getClass().getName()); System.out.println ("Exception message is \"" + e.getMessage() + "\""); } finally { System.out.println ("Performing finalization"); } } } | using System; public class A { public static void Main(string[] args) { try { if(args.Length > 0) Console.WriteLine ("Some arguments are specified"); else throw new ArgumentException ("No arguments specified"); } catch(Exception e) { Console.WriteLine ("Exception caught"); Console.WriteLine ("Exception type is " + e.GetType().FullName); Console.WriteLine ("Exception message is \"" + e.Message + "\""); } finally { Console.WriteLine ("Performing finalization"); } } } |
В Java, начиная с версии 1.4, появилась инструкция assert, предназначенная для выдачи отладочных сообщений. Эта инструкция имеет один из двух видов: assert expression ; assert expression : expression_s ; Выражение expression должно иметь логический тип, а выражение expression_s — произвольный. Проверка таких утверждений может быть выключена. Тогда эта инструкция ничего не делает, и значения входящих в нее выражений не вычисляются. Если проверка утверждений включена, то вычисляется значение expression. Если оно равно true, управление переходит дальше, иначе в обоих случаях выбрасывается исключение java.lang.AssertionError. Во втором случае еще до выброса исключения вычисляется значение выражения expression_s, оно преобразуется в строку и записывается в качестве сообщения в создаваемое исключение. | |
В C# имеется возможность использовать инструкцию goto. Эта инструкция передает управления на инструкцию, помеченную меткой, которая следует за goto. Как мы уже видели, помимо обычных меток, в goto могут использоваться метки case вместе со значениями и метка default. В этих случаях инструкция goto должна находиться внутри блока switch, в котором имеются эти метки. При выходе с помощью goto из блока try или блока catch, у которых имеется соответствующий блок finally, сначала выполняется содержимое этого блока finally. | |
Ключевые слова checked и unchecked в C# могут помечать блок, определяя тем самым контекст вычислений в рамках этого блока (см. раздел о целочисленных типах). | |
Инструкция using может быть использована в C#, чтобы выполнить действия, требующие захвата каких-либо ресурсов, без необходимости заботиться потом об их освобождении. Эта инструкция имеет вид: using ( expression ) statement или using ( declaration ) statement где declaration — это декларация одной или нескольких переменных. Первый вид этой инструкции сводится ко второму — если тип используемого выражения T, и имя v нигде не используется, то он эквивалентен using ( T v = expression ) statement Эта конструкция, в свою очередь, эквивалентна следующей. { T v = expression; try { statement } finally { disposal } } Здесь disposal представляет вызов метода Dispose(), который должен быть у типа T, с возможной предварительной проверкой того, что переменная v не равна null, и приведением ее к типу System.IDisposable, если T является его подтипом. | |
В версии 2.0 в C# введены две инструкции yield, предназначенные для более удобного построения итераторов. Блок, содержащий инструкцию yield, называется итерационным (iterator block) и может быть телом метода, оператора или метода доступа к свойству и не должен быть блоком finally, catch или блоком try, у которого есть соответствующие catch-блоки. Этот блок порождает последовательность значений одного типа. Сам метод или оператор должен возвращать объект одного из четырех типов: System.Collections.IEnumerable, System.Collections.IEnumerator, System.Collections.Generic.IEnumerable <T>, System.Collections.Generic.IEnumerator <T>. В первых двух случаях порождаются объекты типа object, во вторых двух — значения типа T. Для возвращения одного из этой последовательности значений используется инструкция yield return expression; Выражение в ней должно иметь соответствующий тип, object или T. Для указания на то, что порождаемая итерационным блоком последовательность значений завершилась, используется инструкция yield break; Пример реализации итератора коллекции с использованием yield приведен ниже. | |
using System; public class MyArrayList<T> { T[] items = new T[10]; int size = 0; public int Count { get { return size; } } public T this[int i] { get { if(i < 0 || i >= size) throw new IndexOutOfRangeException(); else return items[i]; } set { if(i < 0 || i > size) throw new IndexOutOfRangeException(); else if (i == size) { T[] newItems = new T[size + 10]; Array.Copy (items, newItems, size++); } items[i] = value; } } public IEnumerator<T> GetEnumerator () { for(int i = 0; i < size; i++) yield return items[i]; } } public class A { public static void Main() { MyArrayList<string> l = new MyArrayList<string>(); l[0] = "First"; l[1] = "Second"; l[2] = "Third"; foreach (string s in l) Console.WriteLine(s); } } |