Отличия C# от С++
Приведённая информация может быть неточной и неполной в силу неполноты источника ”
C# language reference”. Где возможна неточность, стоит (?).
C# расширяет возможности и удобства C++. Некоторые вещи в нем реализуются синтаксически по-другому, чем в C++, например, глобальные переменные и функции нужно делать статическими членами классов, вместо включения header-файлов нужно использовать пространства имен. Синтаксис общих по значению конструкций этих языков совпадает за небольшим исключением.
Общий вид программы
Программа является совокупностью текстовых Unicode файлов с расширением .cs. Текст файла содержит директивы препроцессору (все кроме
#include) и сначала проходит через препроцессор.
enum, class, structure, interface, delegate[/COLOR] – определения, из которых состоит текст программы. (Имеется в виду текст, после обработки препроцессором, и, исключая конструкции, связанные с namespace.) Эти определения могут идти в любом порядке и как угодно распределяться по файлам. Все эти определения являются типами, то есть могут быть переменные этих типов. Тело метода не выносится из определения класса, структуры или интерфейса, но оно может импортироваться, см. раздел ”Импорт методов”. Нет глобальных переменных и функций.
namespace, uses
namespace A.B.C {…} Можно определять или расширять сразу вложенное пространство имен. Везде в языке ”::” заменяется на ”.”, по сравнению с C++.
uses D=A.B; Допустимо такое совмещение
uses с определением синонима.
Определения внутри программы
class, structure, interface
struct A {…} Структура отличается от класса тем, что всегда хранится по значению, а не по ссылке и не допускает наследования. После закрывающей фигурной скобки не требуется ”;”.
interface A{…} Интерфейс отличается от класса тем, что не содержит данных, но он может содержать свойства. Классы и интерфейсы могут иметь в качестве родителей интерфейсы.
abstract class A {
public abstract void F();} Абстрактность класса и чистая виртуальность функции указывается словом
abstract.
public class A {…} Определение, видимое в других пространствах имен.
sealed class A {…} Класс, не допускающий наследования (”опечатанный” класс).
delegate
delegate void F(
int); F – делегат. Переменная с типом делегата – совокупность объекта и его метода со спецификацией как у делегата.
class T {
void g(
int n) {…}
void h() {F f=
new F(g); f(5);}} Значение делегата можно создавать только оператором
new. Вызов g(5).
class T1 {
void h() {T x; F f=
new F(x.g); f(5);}} Объект и метод определяются из выражения. Вызов x.g(5).
class T {F f=g;
void g(
int n) {…}} f – переменная типа F.
T x; x.f(5); F f1=x.f;
Определения внутри класса (структуры или интерфейса)
class A{
static int N;
static void F() {…}} Вместо глобальных переменных и функций используются статические члены и методы класса.
class A {
static A() {…}} У класса возможен статический конструктор. Он вызывается автоматически и ровно один раз - до использования данных или ссылок на этот класс или его потомков. Статических деструкторов нет (?).
internal f() {g();}
internal g() {…} Наряду с модификаторами видимости членов класса
public, protected, private используются
internal и
protected internal, которые обеспечивают видимость членов и методов только из методов с таким же модификатором.
class A {
int N=0;} Допускается инициализация членов внутри определения класса.
new void f() {…} Указывается на то, что эта функция другая, чем такая же функция в базовом классе. Модификатор
new может использоваться для любого внутреннего определения класса.
override void f() {…} Эта функция переопределяет или виртуально переопределяет такую же функцию в базовом классе. Если слово
override опущено, результат будет тем же, но компилятор выдаст предупреждение.
unsafe void f(T* a) {a->d=5; *
(int*)(void*)a=1;} Внутри незащищенных процедур возможны обычные для C++ операции с указателями. Компилятор не дает использовать такие операции в других процедурах.
void f(
int x,
int y) {
this.x=x;
this.y=y;} Аргумент метода может иметь такое же имя, как и член класса.
int @double; @double=8; С помощью символа @ могут определяться переменные с именем, являющимся ключевым словом (для совместимости с другими языками).
readonly int n=5; Переменные типа
readonly можно изменять только при инициализации и в конструкторе. Константы (
const) отличаются от
static readonly тем, что могут быть только простых типов.
class, structure, interface могут содержать свойства, события и нумераторы.
Определение операторов
Все операторы должны быть статическими и публичными. Есть три вида операторов: унарные (+, -, !, ~, ++, --, true, false), бинарные (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, <=) и преобразования типа.
public static bool operator true(T t) {
return t==5;} Допустимы унарные операторы true и false.
class T{
public static T1
operator +(T x, T2 y) {…}} Все бинарные операторы имеют такой вид. Хотя бы один аргумент должен быть типа T.
В операторе преобразования типа должно быть указано явный он или неявный:
class T{
int A;
public static bool implicit operator bool() {
return A==5;}
T t;
if(t) …
class T{
int A;
public bool explicit operator bool() {
return A==5;}
T t;
if(bool(t)) …
Свойства
class T{T1 a;
public T1 A {
get{…
return a;},
set{a=value; …}} Определение свойства A. Свойство само по себе не хранит данное.
static T A {
override get{…}
virtual set {…}} Свойства могут быть статическими. Методы
get и
set могут быть
virtual,
override,
abstract.
Нумераторы
class T
{
private string[] items;
public string this[
int index] Пример нумератора.
{
get {
return items[index];}
set{items[index]=value; …}
}
T a; a[5]; Использование нумератора.
Модификаторы аргументов методов
void f(
in int x,
out int y,
ref int z,
params int[][] w) {…} Используются модификаторы аргументов
in,
out,
ref,
params. В качестве
params нельзя использовать многомерные массивы (
int[,]).
f(a,b,
ref c,
new int[][] {{a,b,c},{c,a}}); Ссылка получается с помощью
ref.
Операторы и выражения внутри тела функции
base.f();
base[5]; - доступ к базовому классу осуществляется через ключевое слово
base. Второй пример – использование нумератора базового класса.
Type type=
typeof(тип или выражение); Type – тип System.Type. Из него можно достать разные атрибуты, в том числе, атрибуты пользователя, см. раздел ”Атрибуты”.
if(a
is T) … Переменная a имеет тип T.
try{…
goto a; …}
finaly{…} a: …
finaly выполнится.
switch(n) {
case 0: …;
case 1: …
goto case 0;…} Оператор
goto внутри
switch.
foreach
foreach(T x in a) statement; a – объект, являющийся коллекцией, т.е., имеющий метод GetEnumerator(), который возвращает значение типа, конвертируемого в T, обладающее методом
bool MoveNext(). x – переменная
readonly, используемая в statement, пробегает все перечисляемые значения. Массивы являются коллекциями. Например:
int a[]={2,3,4,5}, s=0;
foreach(
int x
in a) s+=x;
checked, unchecked
int n=1000000; n=
checked(n*n); Генерирует OverflowException.
В отличие от n=
unchecked(n*n);
lock
lock(expression) statement Установка взаимно исключающей блокировки на объект, возвращаемый expression, на время выполнения statement. Например:
lock(
typeof(T)) {…} замок на все объекты класса T, если всегда используется эта блокировка.