public class Plant: IfruitHaver {
[System.Runtime.InteropServices.DllImport('User32.dll)]
public static extern int See();
public Plant(){}
public void Fruit() {
Console.WriteLine('Generic plant fruit');
}
}
Здесь метод See()
помечен как статический. Атрибут DllImport
требует этого от методов, на которых он используется
Пока не существует версии C# ключевых слов transient
, volatile
или synchronized
. Однако существует ряд способов, предоставляемых SDK .NET для имитации некоторой их функциональности. C# использует атрибут NonSerialized
, связанный с полями класса, для предоставления механизма, аналогичного по функциональности модификатору Java transient
, этот атрибут однако опротестован, поэтому может быть изменен в будущих версиях.
Синхронизация в C# несколько усложнена (более трудоемка) по сравнению с ее аналогом в Java. В общем, любой поток выполнения может по умолчанию получить доступ ко всем членам объекта. Имеется, однако ряд способов синхронизации кода в зависимости от потребностей разработчика с помощью использования таких средств, как Monitors
. Они предоставляют возможность делать и освобождать блокировки синхронизации на объектах SyncBlocks
, которые содержат блокировку используемую для реализации синхронизированных методов и блоков кода; список ожидающих потоков выполнения, используемых для реализации функциональности монитора ReaderWriterLock
, который определяет образец одного писателя для многочисленных читателей; примитивы синхронизации Mutex
, предоставляющие межпроцессную синхронизацию; и System.Threading.Interlocked
, который может использоваться для предоставлении синхронизированного доступа к переменным через несколько потоков выполнения.
Первый шаг к синхронизации в C# состоит в ссылке на сборку System.EnterpriseServices.dll
. Инструкция lock(<expression>) {// блок кода}
является единственным, связанным с синхронизацией, ключевым словом в C#. Оно может применяться, также как в Java, для получения взаимно исключающего доступа к блокировке объекта <ref>
. Все попытки получить доступ к <expression>
будут блокированы, пока поток выполнения с блокировкой не освободит ее. Обычно используется выражение либо this
, либо System.Type
, соответствующее Type
представленного объекта. Их использование будет защищать переменные экземпляра выражения в то время, как использование System.Type
будет защищать статические переменные.
Эти модификаторы являются одинаковыми в обоих языках, за исключением инструкции throws
, которая отсутствует в C#. Пугающая вещь в отношении инструкции throws
из Java состоит в том, что она позволяет потребителям компонента с помощью относительно простого синтаксиса использовать компонент, не зная какие исключения он может порождать. Можно удовлетвориться заверениями, что компилированный код обрабатывает все, имеющие отношение к делу, исключения, так как компилятор будет иначе отказывать и информировать обо всех не перехваченных исключениях. Функциональность такого рода отсутствует в C# в настоящее время. Предоставление метода потребителям сборки, желающем знать, когда порождаются исключения, должно будет привести к хорошей практике документирования или некоторому умелому программированию атрибутов.
Выполнение вычислений может привести к сценарию, где вычисленный результат выходит за границы диапазона типа данных переменной результата. В Java, если целые значения достигают своих пределов, то они имеют неприятную особенность переходить к противоположной границе. Чтобы проиллюстрировать это, рассмотрим код следующего класса:
// OverflowEX.java
public class OverfTowEX {
publiс static void main(String args []) {
byte x = 0;
for (int i = 0; i < 130; i++) {
x++;
System.out.println(x);
}
}
}
Как известно, byte
в Java является 8-битовым типом данных со знаком. Это означает, что диапазон значений byte
лежит от -128 до 128. Результатом добавления единицы к любой границе заданного диапазона целого типа будет другая граница диапазона целого типа. Поэтому в этом примере добавление 1 к 127 создаст -128. И если откомпилировать и выполнить эту программу, то последние пять чисел выведенные на консоли будут следующими:
126
127
-128
-127
-126
Это может оказаться весьма существенной проблемой, особенно в связи с тем, что ни предупреждение ни исключение не порождаются, чтобы позволить обработать такое событие (возможно, сохраняя значение в типе с большим диапазоном значений). По умолчанию C# также обрабатывает ситуации переполнения, но язык и компилятор предоставляют инструменты для явной обработки и уведомления программиста в случае переполнения.
□ Программный подход. Чтобы бороться с этим типом молчаливых ошибок, C# вводит концепцию проверяемых и непроверяемых инструкций. Ключевое слово checked
используется для управления контекстом проверки переполнения в случае операций и преобразований арифметики целого типа, таких, как представленные выше. Оно может использоваться как оператор или как инструкция. Инструкции checked/unchecked
следуют приведенному ниже синтаксису (i
). Они предназначены для помещения в скобки ряда инструкций, которые могут создавать переполнение. Синтаксис операции checked/unchecked
показан в пункте (ii
). Операция checked проверяет переполнение одиночных выражений:
(i) checked {block_of_code}
unchecked { block_of_code}
(ii) checked (expression)
unchecked (expression)
block_of_code
содержит код, в котором инструкция checked/unchecked
наблюдает за переполнением, a expression
представляет выражение, в котором checked/unchecked
наблюдает за переполнением в конечном значении. Показанный далее пример иллюстрирует использование checked/unchecked
:
// OverflowEX.cs
public class OverflowEX {
public static void Main(String() args) {
sbyte x = 0; // помните, что необходимо изменить byte на sbyte
for (int i = 0; i < 130; i++) {
checked {
// можно также использовать checked(x++)