C Sharp синтаксис

от Уикипедия, свободната енциклопедия
C#
Парадигмаобектно ориентиран, Структурно програмиране, Императивно програмиране, Функционално програмиране, Родово програмиране
Реализиране през2001
АвторMicrosoft
Типизация на даннитединамична
Програмни диалекти, Spec#, Polyphonic C#
Повлиян отC++, Smalltalk, Eiffel,
Modula-3, Object Pascal
ПовлияваD, F#, Java
УебсайтC# Език (MSDN)

Статията описва синтаксиса на програмния език C#. Описаните характеристики са съвместими .NET Framework и Mono.

Основи[редактиране | редактиране на кода]

Идентификатор[редактиране | редактиране на кода]

Идентификаторът е име на елемент в кода. Съществуват определени стандартни конвенции за именуване , които да се следват при избиране на имена за елементите. Идентификаторът трябва да има описателно име. Препоръчително е да се използват само латински букви. Името му трябва да е нито твърде дълго, нито твърде кратко.

Идентификаторът може да:

  • започва с „_“.
  • съдържа едновременно главни и малки Unicode букви. C# различава главни и малки букви.
  • съдържа цифри [0 – 9].

Идентификаторът не може да:

  • започва с число.
  • започва със символ, освен ако не е ключова дума.
  • има повече от 511 символа.
int New = 2; // правилен идентификато, защото N е главна буква.
string greeting = "Hello"; // правилен идентификатор, името му е достатъчно описателно

int new;	// неправилен идентификатор,new е ключова дума
int 5Ave;	// неправилен идентификатор, името не може да започва с цифра

Ключови думи[редактиране | редактиране на кода]

Ключовите думи са предефинирани запазени идентификатори, които имат специално значение за компилатора.

Езикът има два типа ключови думи – контекстуални и запазени.

Запазените ключови думи като false или byte може да се ползват само като ключови думи.

C# Ключови думи, запазени думи
abstract event new struct
as explicit null switch
base extern object this
bool false operator throw
break finally out true
byte fixed override try
case float params typeof
catch for private uint
char foreach protected ulong
checked goto public unchecked
class if readonly unsafe
const implicit ref ushort
continue in return using
decimal int sbyte virtual
default interface sealed void
delegate internal short volatile
do is sizeof while
double lock stackalloc
else long static
enum namespace string

Обикновено, като се добавят нови ключови думи към езика С #, те се добавят като контекстуални ключови думи, за да се избегне счупване на програми, написани в по-ранни версии.[1] Контекстуалните ключови думи имат специфично значение, само в ограничен програмен контекст, и могат да бъдат използвани като идентификатори извън този контекст.

Някои контекстуални думи, като partial или where, имат специално значение в два или повече контекста.[2]

C# Ключови думи, контекстуални думи
add alias ascending descending
dynamic from get global
group into join let
orderby partial (type) partial (method) remove
select set value var
where (generic type constraint) where (query clause) yield

Ключовите думи не могат да се използват като идентификатори, освен ако не включват @ като префикс.

Но употребата на @-ключови думи за идентификатори не се препоръчва, освен за специални цели. Също знакът @ може да предхожда всеки идентификатор, но това се счита за лоша практика.

Използване на ключова дума като идентификатор:

string @if; // @if е валиден идентификатор, различен от ключовата дума 'if' ,
             // която запазва специфичното си значение

Литерали[редактиране | редактиране на кода]

Литералът (на английски: literal) е константна стойност на променлива, зададена в кода на компютърна програма.

C# Литерали
Булеви
bool true, false
Целочислени
int 12, 1234, -16
uint 12U, 12u
long 12L, 12l
ulong 12UL, 12ul
hexadecimal 0xF5,-0x10, 0x[0..9,A..F,a..f]
Реални
float 23.5F, 23.5f; 1.72e3F, 1.72e3f
double 23.5, 23.5D, 23.5d; 1.72E3D
decimal 24.5m, 1.56M, 1.72E3m, 1.72e3M
exponential notation 6.02e+23, 1.72E3f, 1.72E3m
Дати
date не е възможно
Символни
char 'a', 'Z', '\u0231'
Екранирани (Escaping) последователности
Символ зададен с Unicode \uXХХ
Отместване (табулация) \t
Backspace символ \b
Връщане на реда \r
Form feed \f
Лява наклонена черта \\
Единична кавичка \'
Двойна кавичка \"
Нов ред \n
Низови
string "Hello, world"
"C:\\Windows\\"
@"C:\Windows\"
Nullable/Анулиращи
null

Анулиращите (Nullable) типове са инстанции на структурния декоратор (Wrapper) на System.Nullable върху примитивните типове. Nullabe типът може да представлява нормалния диапазон на стойности за основния тип, плюс допълнително нулева стойност. Nullable типовете са полезни при работа с бази данни или други структури, които имат нулева стойност по подразбиране.

Пример за null с Integer:

int? someInteger = null;
Console.WriteLine(
  "This is the integer with Null value -> " + someInteger);
someInteger = 5;
Console.WriteLine(
  "This is the integer with value 5 -> " +  someInteger);

Променливи[редактиране | редактиране на кода]

Променливите са идентификатори, на които са зададени стойности. Променливата осигурява възможност за:

  • запазване на информация;
  • извличане на запазената информация;
  • модифициране на запазената информация.

Променливите се характеризират с:

  • име (идентификатор);
  • тип (на запазената информация);
  • стойност (запазената информация).

Деклариране

int MyInt; // Деклариране на неинициализирана променлива с име 'MyInt', от тип 'int'

Инициализиране

int MyInt; // Деклариране на неинициализирана променлива
MyInt = 35; // Инициализиране /задаване на начална стойност/ на променливата

Деклариране & Инициализиране

int MyInt = 35; // Едновременно деклариране и инициализиране на променливата

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

int a, b; // Деклариране на няколко променливи от един и същи тип
int a = 5, b = 7; // Деклариране на няколко инициализирани променливи от един и същи тип

Константи[редактиране | редактиране на кода]

Константите са стойности, които са неизменими и не могат да се променят.

const[редактиране | редактиране на кода]

Когато декларираме локална променлива или поле с префикс ключовата дума const, нейната стойност трябва да се даде при декларирането. След това стойността е заключена и не може да бъде променяна. Те трябва да бъдат декларирани в контекста като поле или локална променлива. Константите са имплицитно статични.

const double PI = 3.14;

Следващият пример показва всички употреби на ключовата дума.

class SomeClass
{
    const double x = 3;

    SomeClass()
    {

        const int y = 2;
    }
}

readonly[редактиране | редактиране на кода]

Ключовата дума readonly прави подобно нещо за полетата. Подобно на полетата, маркирани с const, те не могат да се променят веднъж щом им се зададе начална стойност. Разликата е, че може по избор да им се зададе начална стойност в конструктора. Това важи само за полета. Полета, които са само за четене, могат да бъдат, както членове на една инстанция или статични членове на класа.

Код блокове[редактиране | редактиране на кода]

Операторите { ... } се използват за обозначаване на кодов блок и нов обхват. Например, в този кодов блок може да се намират членовете на класа или тялото на метод.

В тялото на метод скобите може да се използват за създаване на нови обхвати:

void someMethod()
{
    int a;

    {
        int b;
        a = 1;
    }

    a = 2;
    b = 3; // Инициализацията ще бъде неуспешна, тъй като променливата b е декларирана във вътрешния обхват.
}

Структура на програмата[редактиране | редактиране на кода]

Едно C# приложение се състои от класове и техните членове. Класовете и другите типове съществуват в именни пространства, но също могат да бъдат вложени в други класове.

Main() метод[редактиране | редактиране на кода]

Всяка програма има входна или стартова точка. В едно C# приложение това е методът Main(). Може да съществува само един Main() метод в клас и той е статичен. Методът обикновено връща void и като списък от параметри да има единствен параметър от тип масив от string.

По-долу е указан правилно дефиниран Main() метод.

static void Main(string[] args)
{
}
//възможно е Main метода да се дефинира без параметри.
static void Main()
{
}

Ако Main() метода не е написан по някой от указаните начини, програмата ще се компилира, но няма да може да се стартира, защото не е дефинирана правилно нейната входна точка.

В Main() метода можем да напишем произволна последователност от изрази, както и методи и те ще бъдат изпълнени в реда, в който сме ги задали.

static int Main()
{
    int a=3;
    int b=2;
    System.Console.WriteLine(a+b);
}

В Main() метода е позволено връщането на integer стойност, ако има такава.

static int Main(string[] args)
{
    return 0;
}

Namespaces[редактиране | редактиране на кода]

Именните пространства са част от типа name и се използват за групиране и / или разграничаване на посочените структури от други такива. Ключовата дума namespace се използва за деклариране на обхват, който съдържа множество от свързани обекти. Namespaces може да се използват за организиране на елементите на кода и да се създадат глобално уникални типове. Namespaces имплицитно са с public достъп и това не може да се променя.

System.IO.DirectoryInfo // DirectoryInfo е в именното пространство System.IO

В namespace може да се декларира един или повече от следните типове:

  • друг namespace
  • class
  • interface
  • struct
  • enum
  • delegate

Именно пространство се дефинира по следния начин :

namespace SampleNamespace
{
    class SampleClass { }

    interface SampleInterface { }

    struct SampleStruct { }

    enum SampleEnum { a, b }

    delegate void SampleDelegate(int i);

    namespace SampleNamespace.Nested
    {
        class SampleClass2 { }
    }
}

using декларация[редактиране | редактиране на кода]

Using декларацията зарежда конкретен namespace от референтни събрания/assembly/. Декларацията обикновено се поставя в горната част (или заглавието) на кода, но може да бъде поставена на друго място, по желание, например вътре в класовете.

using System;
using System.Collections;

Using декларацията също може да се използва за определяне на друго име за съществуващо именно пространство или тип. Това е особено удобно, когато имената са твърде дълги и трудно разбираеми.

using Net = System.Net;
using DirInfo = System.IO.DirectoryInfo;

Оператори[редактиране | редактиране на кода]

Операторите позволяват обработка на примитивни типове данни и обекти. Те приемат един или няколко операнда и връщат някаква стойност.

Категория Оператори
Аритметични
-, +, *, /, %, ++, --
Логически(boolean)
&&,^^,||,!
Побитови (bitwise)
&, |, ^, ~, <<, >>
Съединяване на символни низове
+
Инкрементиране, декрементиране
++ , --
Отместване
<< , >>
За сравняване
==, !=, >, <, >=, <=
Присвояване
=, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
За достъп
.
Индексиране
[ , ]
Кастване
(, )
Условни
?:
Създаване на обекти
new
За работа с типове
(type), as, is, typeof, sizeof
Контрол на Overflow изключения
checked , unchecked 
Насочване и адрес
* , -> , [] , & 
Обединяване/коагулиране
??

Операторите в C# са специални символи и извършват специфични преобразувания над един, два или три операнда.

Тип Операнди
Унарни един аргумент
Бинарни два аргумента
Тернарни три аргумента

Всички бинарни оператори са ляво-асоциативни, освен операторите за присвояване на стойност. Всички оператори за присвояване на стойности и условните оператори ?: и ?? са дясно-асоциативни. Унарните оператори нямат асоциативност.

Използване на оператори:

 int a = 7 + 6;
 Console.WriteLine(a); // 13

 string firstName = "Иван";
 string lastName = "Иванов";

 // между двата низа трябва да има интервал
 string fullName = firstName + " " + lastName;
 Console.WriteLine(fullName); // Иван Иванов

Приоритет на операторите в C#[редактиране | редактиране на кода]

В езика C# някой оператори имат приоритет над други. Операторите с по-висок приоритет се изчисляват преди тези с по-нисък.

Приоритет Оператори
Най-висок
()
++ , -- (постфиксен), new , typeof
++ , -- (префиксен), + – (унарни), ! , ~
 * , / , % 
 + , – 
<< , >>
< , > , <= , >= , is , as
== , != 
& , ^ , |
&&
||
?:
Най-нисък
= , *= , /= , %= , += , -= , <<= , >>= , &= , ^= , |= 

Скобите имат най-висок приоритет. Скобите могат да се използват за промяна на приоритетите на другите оператори. При писане на по-сложни изрази или такива с повече оператори, е препоръчително слагането на скоби, за да се избегнат трудности при четене и разбиране на кода.

Добра практика е използването на скоби, дори да не изглежда необходимо.

//първо ще се изпълни делението
x + y / 100
//скобите правят израза по разбираем
 x + (y / 100)

Овърлоудване на оператори[редактиране | редактиране на кода]

Възможно е някои оператори да бъдат овърлоудвани (претоварени) чрез овърлоуд метод.

public static Boo operator+(Boo boo, Bar bar)
{
    return new Boo(boo.Value + bar.Value);
}
Овърлоудвани оператори
Унарни
 – , + , ! , ~ , ++ , -- , true , false 
Бинарни
 – , + , * , / , % , & , !, ^ , << , >>
За сравняване(овърлоудват се по двойки)
== , != , < , > , <= , >= 
  • Операторите за присвояване (+= , *= , etc.) са комбинация от бинарен оператор и оператор за присвояване (=) и се оценяват чрез използване на обикновени оператори, които могат да бъдат претоварени.
  • Операторите за кастване () не могат да бъдат претоварени, но може да се дефинират операторите за преобразуване.
  • Операторът за индексиране на масив [ ] не може да бъде претоварен, но може да се дефинират нови индексатори.

Оператори за преобразуване[редактиране | редактиране на кода]

Операторът за кастване не може да се претоварва, но е възможно да се напише метод за преобразуване с целевия клас. Методите за преобразуване могат да дефинират два вида оператори за преобразуване: имплицитни (явни) и експлицитни (неявни).

Операторът за имплицитно преобразуване (implicit typecasting) ще преобразува един тип в друг автоматично, без изрично да се използва какъвто и да е оператор. Употребата на имплицитни оператори е позволени, когато няма възможност от загуба на данни. Такова конвертиране е от тип с по-малък обхват към тип с по-голям обхват (int към long) или когато имаме няколко типа данни с различен обхват.

Имплицитно преобразуване

int myInt = 5;
 Console.WriteLine(myInt); // 5

 long myLong = myInt;
 Console.WriteLine(myLong); // 5

 Console.WriteLine(myLong + myInt); // 10

Възможни неявни конверсии са:

  • sbyte > short, int, long, float, double, decimal;
  • byte > short, ushort, int, uint, long, ulong, float, double, decimal;
  • short > int, long, float, double, decimal;
  • ushort > int, uint, long, ulong, float, double, decimal;
  • char > ushort, int, uint, long, ulong, float, double, decimal
  • uint > long, ulong, float, double, decimal;
  • int > long, float, double, decimal;
  • long > float, double, decimal;
  • ulong > float, double, decimal;
  • float > double.

Операторът за експлицитно преобразуване (explicit typecasting) на типове се използва винаги, когато има вероятност за загуба на данни. Подобни загуби са възможни при конверсия на реални към целочислени типове. Или при конверсия от тип с по-голям обхват към тип с по-малък (double към float или long към int).

Експлицитно преобразуване

double myDouble = 6.1d;
 Console.WriteLine(myDouble); // 6.1

 long myLong = (long)myDouble; //експлицитно преобразуване
 Console.WriteLine(myLong); // 6

 myDouble = 6e7d; // 6 * 10^7
 Console.WriteLine(myDouble); // 60000000

 int myInt = (int)myDouble; // експлицитно преобразуване

as оператор[редактиране | редактиране на кода]

Операторът as ще се опита да направи мълчаливо кастване към даден вид. Ако преобразуването е успешно, операторът ще върне обекта като нов тип, ако не е успешно, ще върне нулева референция.

Stream stream = File.Open(@"C:\Temp\data.dat");
FileStream fstream = stream as FileStream; // ще върне обект

String str = stream as String; // ще е неуспешно и ще върне null.

Null-коагулиращия оператор[редактиране | редактиране на кода]

Това е характеристика на C# 2.0.

Null-коагулиращия оператор ?? се използва за определяне на стойността по подразбиране за двата nullable типа стойност и референтни типове.

Операторът връща операнда от лявата страна, ако стойността не е нула. В противен случай той връща операнда от дясната страна.

int? x = null;
int y = x ?? -1;
int? x = 1;
int y = x ?? -1;

Конструкции за управление[редактиране | редактиране на кода]

С # наследява голяма част от конструкциите за контрол от C/C++, а също и добавя нови такива, като foreach.

Условни конструкции[редактиране | редактиране на кода]

Тези конструкции определят начина на изпълнение на програмата по зададени условия.

if конструкция[редактиране | редактиране на кода]

if конструкция се изпълнява, когато зададеното условие е вярно, т.е. има стойност true. Ако в тялото на условната конструкция има само един оператор, не се изискват блок скоби, въпреки че е препоръчително да се слагат блок скоби.

Конструкция с един оператор:

if (i == 3) ...;

Конструкция с else блок (без каквито и да е скоби):

if (i == 2)
    ...
else
    ...

Използването на поредица от if конструкции:

if (i == 3)
{
    ...
}
else if (i == 2)
{
    ...
}
else
{
    ...
}

switch конструкция[редактиране | редактиране на кода]

Switch конструкция служи като филтър за различни стойности. Всяка стойност довежда до „случай“ – case етикет. Не е позволено отпадане на случаи и затова обикновено се използва ключовата дума break за край в case етикет. Неусловно return в case блок също може да се използва за край. Може да се разгледа как goto инструкция може да се използва, за преминаване от един case етикет към друг. Много case етикети може да доведат до еднакав код обаче. Case етикетът – по подразбиране изпълнява всички други случаи, които не се обработват от конструкцията.

switch (ch)
{
    case 'A':
        statement;
        ...
        break;
    case 'B':
        statement;
        break;
    case 'C':
        ...
        break;
    default:
        ...
        break;
}

Цикъл конструкция[редактиране | редактиране на кода]

Цикъл конструкциите са конструкции, които са многократно изпълнявани, докато е в сила дадено условие.

while цикъл[редактиране | редактиране на кода]

while (i == true)
{
    ...
}

do ... while цикъл[редактиране | редактиране на кода]

do
{
    ...
}
while (i == true);

for цикъл[редактиране | редактиране на кода]

for цикъл се състои от три части: деклариране, условие и обновяване на променлива. Всяка от тях може да не бъде взета предвид, тъй като не са задължителни.

for (int i = 0; i < 10; i++)
{
    ...
}

Това е еквивалент на следната while конструкция.

int i = 0;
while (i < 10)
{
    //...
    i++;
}

foreach цикъл[редактиране | редактиране на кода]

foreach цикъл произлиза от for цикъл и следва определен модел, описан в спецификацията на езика С# за обхождане на всички елементи.

foreach (int i in intList)
{
    ...
}

Jump конструкции[редактиране | редактиране на кода]

Jump конструциите са наследени от C/C++ и са в основата на асемблираните езици. Те представляват jump инструкция – незабавно променяне хода на изпълнение на програмата.

Етикети и goto инструкция[редактиране | редактиране на кода]

Етикетите са определени точки в кода, към които може да се промени хода на изпълнение напрограмата с помощта на goto инструкция.

start:
    ...
    goto start;

goto инструкцията може да се използва в switch конструкция за прехвърляне контрола от един към друг case етикет.

switch(n)
{
    case 1:
        Console.WriteLine("Case 1");
        break;
    case 2:
        Console.WriteLine("Case 2");
        goto case 1;
    case 3:
        Console.WriteLine("Case 3");
    case 4: // Compilation will fail here as cases cannot fall through in C#.
        Console.WriteLine("Case 4");
        goto default; // This is the correct way to fall through to the next case.
    default:
        Console.WriteLine("Default");
}

break инструкция[редактиране | редактиране на кода]

break инструкцията прекратява най-близко обхващащия цикъл или switch конструкция. Управлението се предава на конструцията, която следва след приключената, ако има такива.

int e = 10;
for (int i = 0; i < e; i++)
{
    while (true)
    {
        break;
    }
    // Ще се прекъсне в тази точка
}

continue инструкция[редактиране | редактиране на кода]

continue инструкцията прекъсва текущата итерация на текущата конструкция за управление и започва следващата итерация.

int ch;
while (0)
{
    if (ch == ' ')
        continue;    // прескача останалия код от while цикъла

    ...
}

while цикълът в кода по-горе чете символи с метода GetChar() и ако символът е празно пространство, ще се прескочат останалите инструкции в тялото на цикъла.

Прихващане на изключения[редактиране | редактиране на кода]

Методът за прихващане на изключения по време на изпълнение на програмата в С#, е наследен от Java и C/C++.

Класът System.Exception е базовият клас за всички изключения. Обектът изключение съдържа цялата информация за точно определено изключение, както и обвито изключение, ако определеното изключение съдържа причина за възникването му.

Програмистите могат да определят свои собствени изключения.

Изключение може да бъде прихванато по следния начин

    throw new NotImplementedException();

try ... catch ... finally конструкция[редактиране | редактиране на кода]

Изключенията се управляват в try ... catch ... finally блок.

try
{
    // Statements which may throw exceptions
    ...
}
catch (Exception ex)
{
    // Exception caught and handled here
    ...
}
finally
{
    // Statements always executed after the try/catch blocks
    ...
}

Кодът в try блока се изпълнява и ако се появи изключение, изпълнението на блока се прекъсва и изключението се прихваща в catch блока. Може да има много catch блокове, като в този случай се изпълнява първия блок с дефинирана променлива за изключение, чиито тип съвпада с типа на хвърленото изключение.

Ако няма catch блок, който да съответства на типа на хвърленото изключение, изпълнението на външния блок (или метод), съдържащ try...catch конструкцията е прекъсва и изключението се предава извън съдържащия блок или метод. Може да има много catch блокове, като в този случай се изпълнява първия блок с дефинирана променлива за изключение, чиито тип съвпада с типа на хвърленото изключение. Изключението се препредава, докато не намери съвпадение в някой от текущо активните методи. Ако изключението достигне главния метод – Main() без съвпадение, тогава цялата програма се прекратява и текстово описание на изключението се изписва на стандартния изходен поток.

Кодът в finally блока се изплънява винаги, независимо дали е прихванато изключение.

Или catch блок, или finally блок, или и двата трябва да следват try блок.

Типове[редактиране | редактиране на кода]

C # е статично типов език като C и C + +. Това означава, че на всяка променлива или константа трябва да се зададе тип, когато се декларира. Типовете данни в C# са два вида: стойностни и референтни.

Стойностни типове[редактиране | редактиране на кода]

Стойностните типове се съхраняват в стека, съдържат директно своята стойност и се предават по стойност, т.е. ако се декларира променлива от стойностен тип, то стойността се съхранява директно в паметта. Стойностните типове се освобождават при излизане от обхват, т.е. стойността на декларираната променлива се изтрива от стека.

Структури[редактиране | редактиране на кода]

Структурите са по-известни като structs. Структурите са дефинирани от потребителя стойностни типове, които се декларират със запазената дума struct. Те са много подобни на класовете, но са по-ограничени. Някои важни синтактични разлики между клас и структура са представени по-късно в тази статия.

struct Foo
{
    ...
}
Вградени типове[редактиране | редактиране на кода]

Те са примитивни типове данни.

Примитивни типове
Тип данни BCL Съответствие Стойност Обхват Размер Стойност по подразбиране
sbyte System.SByte цяло число −128 ÷ +127 8-bit (1-byte) 0
short System.Int16 цяло число −32,768 ÷ +32,767 16-bit (2-byte) 0
int System.Int32 цяло число −2,147,483,648 ÷ +2,147,483,647 32-bit (4-byte) 0
long System.Int64 цяло число −9,223,372,036,854,775,808 ÷
+9,223,372,036,854,775,807
64-bit (8-byte) 0
byte System.Byte цяло число без знак 0 ÷ 255 8-bit (1-byte) 0
ushort System.UInt16 цяло число без знак 0 ÷ 65,535 16-bit (2-byte) 0
uint System.UInt32 цяло число без знак 0 ÷ 4,294,967,295 32-bit (4-byte) 0
ulong System.UInt64 цяло число без знак 0 ÷ 18,446,744,073,709,551,615 64-bit (8-byte) 0
decimal System.Decimal реално число с десетична точност −79,228,162,514,264,337,593,543,950,335 ÷
+79,228,162,514,264,337,593,543,950,335
128-bit (16-byte) 0.0
float System.Single реално число с единична точност ±1.401298E−45 ÷ ±3.402823E+38 32-bit (4-byte) 0.0
double System.Double реално число с двойна точност ±4.94065645841246E−324 ÷
±1.79769313486232E+308
64-bit (8-byte) 0.0
bool System.Boolean булева true or false 8-bit (1-byte) false
char System.Char Unicode символ '\u0000' ÷ '\uFFFF' 16-bit (2-byte) '\u0000'

Забележка: string (System.String) не е структура, както и не е примитивен тип.

Изброени типове[редактиране | редактиране на кода]

Изброените типове (enums) са наименувани константи, представящи цели числа.

enum Season
{
    Winter = 0,
    Spring = 1,
    Summer = 2,
    Autumn = 3,
    Fall = Autumn
}

Променливите от изброен тип имат стойност по подразбиране нула. Те могат да бъдат декларирани или инициализирани към наименувана константа, определена от изброения тип.

Season season;
season = Season.Spring;

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

season = (Season)2; // преобразуване на 2 в стойност на изброен тип – Season.
season = season + 1; // добавяне на 1 към стойността.
season = season + season2; // добавяне на стойностите на две променливи от изброен тип.
int value = (int)season; // преобразуване на стойност от изброен тип в целочислена стойност.

season++; // Season.Spring (1) става Season.Summer (2).
season--; // Season.Summer (2) става Season.Spring (1).
Color myColors = Color.Green|Color.Yellow|Color.Blue;

Вижте също

Референтни типове[редактиране | редактиране на кода]

Променливи референтен тип имат строго типизиран указател (адрес на клетка от паметта). Когато конструкторът е извикан, се създава обект в динамичната памет и указател към променливата, т.е. референтните типове съдържат в стека указател към динамичната памет, където се съхранява тяхната стойност. Когато променливата на обект излиза от обхват указателят се разкача от стойността си и когато няма указател, свързан към обекта, то той остава свободен и се маркира като боклук. По някое време системата за почисване на паметта ще изтрие тези обекти.

Променлива от референтен тип приема стойност null, когато няма указател към обект.

Масиви[редактиране | редактиране на кода]

Типът масив е референтен тип, който се отнася за група от един или повече елементи от един и същ тип. Всички типове масиви произхождат от общ базов клас System.Array. Всеки елемент е посочен от своя индекс, както е при C++ и Java.

Масив в C# е това, което се нарича динамичен масив при C++.

int[] numbers = new numbers[0] = 2;
numbers[1] = 5;
int x = numbers[0];
Инициализация[редактиране | редактиране на кода]

Масивите имат удобен синтаксис за инициализация на масиви.

// пълен синтаксис
int[] numbers = new int[5]{ 20, 1, 42, 15, 34 };
// кратък синтаксис
int[] numbers2 = { 20, 1, 42, 15, 34 };
Многомерни масиви[редактиране | редактиране на кода]

Масивите могат да имат различни размерности, например двумерните масиви представляват матрица.

int[,] numbers = new int[3, 3];
numbers[1,2] = 2;

int[,] numbers2 = new int[3, 3] { {2, 3, 2}, {1, 2, 6}, {2, 4, 5} };

Класове[редактиране | редактиране на кода]

Класовете са самостоятелно описващи се, дефинирани от потребителя референтни типове. По същество всички типове в .NET Framework са класове, включително структури и изброени типове, които са генерирани от компилатора класове. Елементите на класа са private по подразбиране, но могат да бъдат декларирани като public, за да се виждат извън класа или protected, за да се виждат, от който и да е от наследниците на класа.

Клас String[редактиране | редактиране на кода]

Класът System.String или накратко string е неизменима последователност от символи (char), представени в Unicode таблицата.

Действия, извършени върху променлива от тип string, винаги ще връща нова такава.

    string text = "Hello World!";
    string substr = text.Substring(0, 5);
    string[] parts = text.Split(new char[]{ ' ' });

Класът System.StringBuilder може да се използва, когато се налага променяне на символни низове.

    StringBuilder sb = new StringBuilder();
    sb.Append('H');
    sb.Append("el");
    sb.AppendLine("lo!");

Интерфейс[редактиране | редактиране на кода]

Интерфейсът е структура от данни, която съдържа дефиниция на роля (на група абстрактни действия) без действително осъществяване. Променлива на интерфейсен тип е указател към инстанция на клас, който имплементира този интерфейс. Вижте #Интерфейси.

Делегати[редактиране | редактиране на кода]

Основна статия: Delegate (CLI)

C# предоставя типово-безопасни, обектно-ориентирани указатели към функции под формата на делегати.

class Program
{
    // Delegate type.
    delegate int Operation(int a, int b);

    static int Add(int i1, int i2)
    {
        return i1 + i2;
    }

    static int Sub(int i1, int i2)
    {
        return i1  i2;
    }

    static void Main()
    {
        // Инстанциране на делегат и назначаване на метод към него.
        Operation op = Add;

        // Извикване на посочения от делегата метод.
        int result1 = op(2, 3);  // 5

        op = Sub;
        int result2 = op(10, 2); // 8
    }
}

Инициализиране на делегат като анонимен метод.

 addition = delegate(int a, int b){ return a + b; };

Събития[редактиране | редактиране на кода]

Събитията са указатели, които могат да сочат към многобройни методи. По-точно те обединяват тези указатели към един идентификатор. Това може да се разглежда като разширение на делегатите. Събитията в C# са специални инстанции на делегати, декларирани с ключовата дума event. Те обикновено се използват като тригери в UI разработването. Формата, използвана в C # и други езици от Common Language Infrastructure, се основава на това в класическия Visual Basic.

delegate void MouseEventHandler(object sender, MouseEventArgs e);

public class Button : System.Windows.Controls.Control
{
    event MouseEventHandler OnClick;

    /* Imaginary trigger function */
    void click()
    {
        this.OnClick(this, new MouseEventArgs(data));
    }
}

Събитието обикновено е придружено от метод, който ще манипулира събитие. Този метод представлява специален делегат, който има 2 параметъра – изпращач и аргументи на събитието. Типът на аргумент-обекта на събитието произхожда от класа EventArgs, който е част от CLI base library.

Веднъж декларирано в класа си, единствения начин да се извика събитие е от притежателя, т.е. от класа, в който е дефинирано. Методът за получаване на събитие може да бъде имплементиран извън, за да може бъде стартиран, когато събитието е изгубено.

public class MainWindow : System.Windows.Controls.Window
{
    private Button button1;

    public MainWindow()
    {
        button1 = new Button();
        button1.Text = "Click me!";

        /* Абониране за събитие */
        button1.ClickEvent += button1_OnClick;

        /* Алтернативен синтаксис, който се приема за остарял:
           button1.MouseClick += new MouseEventHandler(button1_OnClick); */
    }

    protected void button1_OnClick(object sender, MouseEventArgs e)
    {
        MessageBox.Show("Clicked!");
    }
}

Вижте също

Нулеви типове[редактиране | редактиране на кода]

Това е опция на C# 2.0.

Нулевите типове са представени в C# 2.0. първоначално, за да се позволи стойностните типове да приемат стойност null, когато се работи с база данни.

int? n = 2;
n = null;

Console.WriteLine(n.HasValue);

Всъщност това е същото като използването на Nullable<T> структура.

Nullable<int> n = 2;
n = null;

Console.WriteLine(n.HasValue);

Указатели[редактиране | редактиране на кода]

C # има и позволява указатели към стойностни типове (примитивни, enums и structs) в не безопасен контекст: методи и кодов блок, маркирани с unsafe. Синтактично са същите като указателите в C и C++. Прекъсване по време на компилиране е изключено в не безопасен контекст.

static void Main(string[] args)
{
    unsafe
    {
        int a = 2;
        int* b = &a;

        Console.WriteLine("Address of a: {0}. Value: {1}", (int)&a, a);
        Console.WriteLine("Address of b: {0}. Value: {1}. Value of *b: {2}", (int)&b, (int)b, *b);

        // На конзолата ще се изведе:
        // Address of a: 71953600. Value: 2
        // Address of b: 71953596. Value: 71953600. Value of *b: 2
    }
}

При използването на структури се изскват само структури без елементи от референтен тип като символен низ или друг клас.

public struct MyStruct
{
    public char Character;
    public int Integer;
}

public struct MyContainerStruct
{
    public byte Byte;
    public MyStruct MyStruct;
}

Начин на използване:

MyContainerStruct x;
MyContainerStruct* ptr = &x;

byte value = ptr->Byte;

Вижте също

Dynamic[редактиране | редактиране на кода]

Това е опция на C# 4.0 и .NET Framework 4.0.

Типът dynamic позволява динамични справки за С# по време на компилиране. Dynamic е статичен тип, който съществува само по време на компилиране.

dynamic x = new Foo();
x.DoSomething(); // Ще се компилира и анализира по време на изпълнение. Ще хвърли изключение, ако е невалиден.

Анонимни типове[редактиране | редактиране на кода]

Това е опция на C# 3.0.

Анонимните типове са безименни класове, които се генерират от компилатора. Използват се за еднократна употреба. Те са много полезни в случаи, когато при LINQ заявка, която връща резултат под формата на обект с select трябва да върнат специални стойности. След това може да се определи анонимен тип, съдържащ автоматично генерирани, само за четене полета за тези стойности.

При инстанциране на друга декларация от анонимен тип със същата сигнатура, типът се подразбира от компилатора.

var carl = new { Name = "Carl", Age = 35 }; // Името на типа е известно само на компилатора.
var mary = new { Name = "Mary", Age = 22 }; // Същият тип като горния израз.

Опаковане и Разопаковане[редактиране | редактиране на кода]

Опаковането е операция за преобразуване на променлива от стойностен тип в обектен тип, който представлява референтен тип.[3] Опаковането в C# е неявно преобразуване.

Разопаковането е операция за преобразуване на променлива от референтен тип (преди това трябва да е изпълнена операция Опаковане) в стойностен тип.[3] Разопаковането в C# изисква изрично (явно) преобразуване.

Пример:

int foo = 42; // Променлива от стойностен тип.
object bar = foo; // Променливата foo е опакована.
int foo2 = (int)bar; // Рразопаковане обратно в стойностен тип.

Обектно-ориентирано програмиране (ООП)[редактиране | редактиране на кода]

C# има директна поддръжка за обектно-ориентирано програмиране.

Обекти[редактиране | редактиране на кода]

Обектът е създаден по шаблона на даден клас и е инстанция на този клас.

В C# обектите са или указатели, или стойности. Не се обособяват други синтактични особености между тях в кода.

Клас Object[редактиране | редактиране на кода]

Всички типове, дори и стойностните типове в опакована форма, неявно наследяват System.Object класа, който е основният базов клас на всички обекти. Класът съдържа най-общите методи, споделяни от всички обекти. Някои от тях са виртуални и могат да бъдат отменени.

Класовете наследяват System.Object пряко или непряко чрез друг базов клас.

Методи
Основни методи на клас Object:

  • Equals – поддържа сравняване между обекти.
  • Finalize – извършва операции по почистване преди обект да бъде автоматично използван отново. (Деструктор по подразбиране)
  • GetHashCode – връща число, съответстващо на стойността на обекта, относно използването на хеш таблица.
  • GetType – връща типът на текущата инстанция.
  • ToString – връща символен низ, който представлява текущия обект.

Класове[редактиране | редактиране на кода]

Класовете са основите на един обекто-ориентиран език, какъвто е C#. Те служат като шаблон, чрез който се описват обекти. Съдържат компоненти, които съхраняват и манипулират данни по реалистичен начин.

Вижте също

Разлики между класове и структури[редактиране | редактиране на кода]

Въпреки че класовете и структурите са подобни, както по начина на деклариране, така и по това как се използват, има някои съществени разлики. Класовете са референтен тип, а структурите – стойностен тип. Структурата се съхранява в стека, когато се декларира и съдържа директно стойността си. Класовете са различни, защото съдържат в стека указател към динамичната памет, където се съхранява стойността. Указателят има тип и може да съдържа само обекти от своя тип.

Структурите изискват малко повече писане, отколкото класовете. Например трябва изрично да се създаде конструктор по подразбиране, който не изисква аргументи, за да се инициализира структурата и нейните елементи. Компилаторът ще създаде такъв по подразбиране за клас. Всички полета и свойства на една структура трябва да бъдат инициализирани преди да се създаде инстанция. Структурите не притежават финализатори и не могат да наследяват от други класове, както е при класовете. Въпреки това, те наследяват от System.ValueType, който на своя страна наследява System.Object. Структурите са по-подходящи за малки конструкции от данни.

Това е кратко обобщение на разликите:

Конструктор по подразбиране Финализатор Инициализиране на елементи Наследяване
Class не се изисква (автоматично се генерират) да не се изисква да (ако базовият клас не е sealed)
Struct изисква се (не се генерира автоматично не изисква се не се поддържа

Деклариране[редактиране | редактиране на кода]

Клас се декларира по следния начин:

class Foo
{
    // Деклариране елементите на класа
}
Частичен клас[редактиране | редактиране на кода]
Това е опция на C# 2.0.

Частичен клас е деклариране на клас, чиито код е разделен в отделни файлове. Различните части на частичния клас трябва да бъдат дефинирани с ключова дума partial.

// File1.cs
partial class Foo
{
    ...
}

// File2.cs
partial class Foo
{
    ...
}

Инициализиране[редактиране | редактиране на кода]

Преди да може да се използват елементите на класа, трябва да се инициализират променливите с указател към обект. За да се създаде обект, трябва да се извика съответния конструктор, използвайки ключова дума new. Конструкторът има същото име, както на класа.

Foo foo = new Foo();

За структурите не е задължително да се извиква конструктор, защото той се извиква автоматично по подразбиране. Трябва само да се декларира и констукторът се инициализира със стандартни стойности.

Обектни инициализатори[редактиране | редактиране на кода]
Това е опция на C# 3.0.

Предлага се по-удобен начин за инициализиране на публични полета и свойства на даден обект. Извикването на конструктор не е задължително, когато има такъв по подразбиране.

Person person = new Person {
    Name = "John Doe",
    Age = 39
};

// Еднозначно с:
Person person = new Person();
person.Name = "John Doe";
person.Age = 39;
Инициализатори на колекции[редактиране | редактиране на кода]
Това е опция на C# 3.0.

Инициализаторите на колекции имат синтаксис, подобен на този на масивите за инициализиране на колекции. Компилаторът ще генерира само извикване на Add-method. Това се отнася за класове, които имплементират ICollection интерфейс.

List<int> list = new List<int>{2, 5, 6, 6};

// Еднозначно с:
List<int> list = new List<int>();
list.Add(2);
list.Add(5);
list.Add(6);
list.Add(6);

Начини за достъп[редактиране | редактиране на кода]

Елементите на дадена инстанция и статичните компоненти на класа са достъпни чрез използването на „.“ оператор.

Достъп до елемент на инстанция
Елементите на дадена инстанция са достъпни чрез името на променлива.

string foo = "Hello";
string fooUpper = foo.ToUpper();

Достъп до статичен компонент на клас
Статичните компоненти са достъпни чрез използването на името на класа или друг тип.

int r = String.Compare(foo, fooUpper);

Достъп до компонент чрез указател
В не безопасен контекст, членовете на стойността (структурен тип) указани чрез указател, са достъпни чрез „->“ оператор, точно както при C и C++.

POINT p;
p.X = 2;
p.Y = 6;
POINT* ptr = &p;
ptr->Y = 4;

Модификатори[редактиране | редактиране на кода]

Модификаторите са ключови думи, които се използват за изменение в декларирането за типове. Особено забележимо е наличието на подгрупа, съдържаща модификатори за достъп.

Модификатори за класове[редактиране | редактиране на кода]
  • abstract – определя, че клас служи само като базов клас. Трябва да бъде имплементиран в наследяващ клас.
  • sealed – определя, че клас не може да бъде наследен.
Модификатори за компоненти на клас[редактиране | редактиране на кода]
  • const – определя, че променлива има непроменлива стойност, която трябва да бъде инициализирана, когато се декларира.
  • event – декларира събитие.
  • extern – определя, че сигнатурата на метод без тяло използва DLL-import.
  • override – определя, че метод или служи за пренаписване на виртуален компонент или имплементиране на компонент от абстрактен клас.
  • readonly – декларира поле, на което могат само да бъдат зададени стойности като част от декларирането или в конструктор в същия клас.
  • unsafe – определя не безопасен контекст, който позволява употребата на указатели.
  • virtual – определя, че метод може да бъда пренаписан от класа, от който произхожда.
  • volatile – определя поле, което може да бъде изменено от външен процес и предотвратява компилатора да оптимизира промяна в употребата на полето.
Статичен модификатор[редактиране | редактиране на кода]

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

public class Foo
{
    public static void Something()
    {
        ...
    }
}
// извикване метод на клас
Foo.Something();
Модификатори за достъп[редактиране | редактиране на кода]

Модификаторите за достъп, или модификатори при наследяване, определят достъпността на класовете, методите и другите компоненти. Компонент, дефиниран с public може да бъде достъпен от всякъде. Private компонентите могат да бъдат достъпни само от класа, в който са декларирани и ще бъдат скрити при наследяване. Компоненти с модификатор за достъп protected ще бъдат невидими, но достъпни при наследяване. Класове и членове с модификатор на достъп internal ще са достъпни само от ползватели на едно и също асембли.

Класовете и структурите са изрично internal, а компонентите – private, ако нямат зададен модификатор за достъп.

public class Foo
{
    public int Do()
    {
        return 0;
    }

    public class Bar
    {

    }
}

Следната таблица показва къде могат да бъдат използвани модификаторите за достъп.

Невложени типове Компоненти (вкл. вложени типове)
public да да
private не да (по подразбиране)
protected не да
internal да (по подразбиране) да
protected internal не да

Конструктори[редактиране | редактиране на кода]

Конструкторът е специален метод, който се извиква автоматично, когато се създава обект. Неговата цел е да инициализира елементите на обекта. Конструкторите имат същото име като на класа и не връщат нищо. Могат да приемат параметри като всеки друг метод.

class Foo
{
    Foo()
    {
        ...
    }
}

Конструкторите могат да бъдат public, private или internal.

Вижте също

Деструктор[редактиране | редактиране на кода]

Деструкторът се вика, когато обектът ще се събира от системата за почисване на паметта, за да се извърши ръчно почистване. Има метод деструктор по подразбиране наречен финализатор, който може да се пренапише чрез деклариране на самия себе си.

Синтаксисът е подобен на този на конструкторите. Разликата е в това, че името се предхожда от „~“ и не може да съдържа никакви параметри. Не може да има повече от един деструктор.

class Foo
{
    ...

    ~Foo()
    {
        ...
    }
}

Финализаторите са винати private.

Вижте също

Методи[редактиране | редактиране на кода]

Както в C и C++, има функции, които групират многократно използван код. Основната разлика е, че тези функци, точно както е при Java, трябва да пребивават в рамките на класа. По тази причина функцията се нарича метод. Методът има връщана стойност, име и обикновено някакви параметри, които се инициализират, когато методът се извиква с аргументи. Може или да принадлежи на инстанция на клас или да бъде статична компонента.

class Foo
{
    int Bar(int a, int b)
    {
        return a%b;
    }
}

Метод се извиква с . нотация на зададената променлива, или при статични методи – име на типа.

Foo foo = new Foo();
int r = foo.Bar(7, 2)

Console.WriteLine(r);

Вижте също

ref и out параметри[редактиране | редактиране на кода]

Аргументи могат да се предават по референция, когато при извикване на метод към описанието на параметър в дефиницията на метода се добави ключова дума ref или out. Удобно е да се използват, когато се налага в даден метод стойността на променлива да се променя по референция, т.е. тази промяна директно се отразява на променливата. Разликата между тези два параметъра е, че преди инициализацията при out достъпът е само за писане, докато при ref достъпът е за четене и писане.

void PassRef(ref int x)
{
    if(x == 2) x = 10;
}
int Z;
PassRef(ref Z);

void PassOut(out int x)
{
    x = 2;
}
int Q;
PassOut(out Q);
Незадължителни параметри[редактиране | редактиране на кода]
Това е опция на C# 4.0.

C # 4.0 въвежда незадължителни параметри със стойности по подразбиране, както е в C++. Например:

void Increment(ref int x, int dx = 1)
{
  x += dx;
}

int x = 0;
Increment(ref x); // dx приема стойност по подразбиране 1
Increment(ref x, 2); // dx приема стойност 2

Незадължителните параметри позволяват пропускането на пара­метри при извикване на метод. Освен това е възможно подаване на стойност на параметър чрез името му (именуван параметър) при извикване на метода, което позволява селективно преминаване през всяко подмножество от незадължителни параметри. Единственото ограничение е, че именуваните параметри трябва да бъдат поставени след неименуваните параметри. Именуваните параметри могат да определят незадължителните и задължителните параметри, както и да бъдат използвани за произволна наредба на аргументи при извикване на метода. Например:

Stream OpenFile(string name, FileMode mode = FileMode.Open,
FileAccess access = FileAccess.Read) { ... }

OpenFile("file.txt"); // use default values for both "mode" and "access"
OpenFile("file.txt", mode: FileMode.Create); // use default value for "access"
OpenFile("file.txt", access: FileAccess.Read); // use default value for "mode"
OpenFile(name: "file.txt", access: FileAccess.Read, mode: FileMode.Create);

Незадължителните параметри правят по-лесна съвместната работа с COM. По-рано, трябваше да се премине през всеки параметър в метода на COM компонента, дори и през тези, които не са задължителни. Например:

object fileName = "Test.docx";
object missing = System.Reflection.Missing.Value;

doc.SaveAs(ref fileName,
    ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing);
console.writeline("File saved successfully");

С незадължителните параметри, кодът може да се съкрати така:

doc.SaveAs(ref fileName);
extern[редактиране | редактиране на кода]

Опция на C #, е способността да се извиква собствен код. Сигнатурата на метода е проста, методът се декларира без тяло и е дефиниран с extern. Атрибутът DllImport също трябва да се добави към указателя на дадения DLL файл.

[DllImport("win32.dll")]
static extern double Pow(double a, double b);

Полета[редактиране | редактиране на кода]

Полетата са променливи, който се декларират в класа и съхраняват данни

class Foo
{
    double foo;
}

Полетата могат да бъдат инициализирани директно, когато се декларират.

class Foo
{
    double foo = 2.3;
}

Модификатори за полета:

  • const – прави полето непроменливо.
  • private – прави полето частно (по подразбиране).
  • protected – прави полето защитено.
  • public – прави полето публично.
  • readonly – разрешава на полето да бъде инициализирано само веднъж в конструктора.
  • static – прави полето статична компонента.

Свойства[редактиране | редактиране на кода]

Свойствата имат синтаксис като този на полетата и възможностите на методите. Свойството може да има два метода за достъп: get и set.

class Person
{
    string name;

    string Name
    {
        get { return name; }
        set { name = value; }
    }
}

// Използване на свойство
Person person = new Person();
person.Name = "Robert";

Модификатори за свойства:

  • private – прави свойството частно (по подразбиране).
  • protected – прави свойството защитено.
  • public – прави свойството публично.
  • static – прави свойството статичен компонент.

Модификатори за методи за достъп до свойство:

  • private – прави метода за достъп частен.
  • protected – прави метода за достъп защитен.
  • public – прави метода за достъп публичен.

Модификаторите за методи за достъп до свойство по подразбиране са наследени от свойството. Трябва да се има предвид, че модификаторът за методи за достъп може да бъде само или еднакъв, или ограничен от модификатора за свойството.

Автоматични свойства[редактиране | редактиране на кода]
Това е опция на C# 3.0.

Тази опция на C# 3.0 включва автоматично имплементирани свойства. Дефинират се методи за достъп до свойство без тяло и компилаторът ще генерира полета и необходимия код за тези методи.

public double Width
{
    get;
    private set;
}

Индексатори[редактиране | редактиране на кода]

Индексаторите добавят възможностите на индексирането при масиви на обектите. Те се имплементират по начин подобен на свойствата.

class IntList
{
   int[] items;

   int this[int index]
   {
        get { return this.items[index]; }
        set { this.items[index] = value; }
    }
}

// използване на индексатор
IntList list = new IntList();
list[2] = 2;

Наследяване[редактиране | редактиране на кода]

Класовете в C# могат да наследяват само от един клас. Класът може да произхожда от кой да е клас, който не е дефиниран с sealed.

class A
{

}

class B : A
{

}

Вижте също

virtual[редактиране | редактиране на кода]

Методи, които са дефинирани с virtual се изпълняват, но те могат да бъдат пренаписани от наследниците с ключовата дума override.

Имплементацията се определя по действителния тип на обекта, а не по типа на променливата.

class Operation
{
    public virtual int Do()
    {
        return 0;
    }
}

class NewOperation : Operation
{
    public override int Do()
    {
        return 1;
    }
}
new[редактиране | редактиране на кода]

При вариант на невиртуален метод с друга сигнатура, може да се използва ключова дума new. Методът, който ще се използва, ще се определи по тип на променливата, вместо по действителния тип на обекта.

class Operation
{
    public int Do()
    {
        return 0;
    }
}

class NewOperation : Operation
{
    public new double Do()
    {
        return 4.0;
    }
}

Това показва примера:

NewOperation operation = new NewOperation;

// Will call "double Do()" in NewOperation
double d = operation.Do();

Operation operation_ = operation;

// Will call "int Do()" in Operation
int i = operation_.Do();
abstract[редактиране | редактиране на кода]

Абстрактните класове са класове, които служат само като шаблони и няма да може да се инициализира обект от този тип. В противен случай са точно като обикновен клас.

Може да има също абстрактни компоненти. Абстрактните компоненти са компоненти на абстрактни класове, които нямат никаква имплементация. Те трябва да бъдат пренаписани от класа, който наследява компонента.

abstract class Mammal
{
    public abstract void Walk();
}

class Human : Mammal
{
    public override void Walk()
    {

    }

    ...
}
sealed[редактиране | редактиране на кода]

Mодификаторът sealed може да се комбинира с други като незадължителен модификатор за класове, за да ги направи ненаследими.

internal sealed class _FOO
{

}

Интерфейси[редактиране | редактиране на кода]

Интерфейсите са структури от данни, които съдържат дефиниции на роли, които не се имплементират действително. Те са полезни, когато трябва да се определи договор между компоненти с различни типове, които имат различни имплементации. Може да се декларират дефиниции за методи, свойства и индексатори. Интерфейсните компоненти са publuc. Интерфейсът може да бъде имплементиран неявно или явно.

interface IBinaryOperation
{
    double A { get; set; }
    double B { get; set; }

    double GetResult();
}

Имплементиране на интерфейс[редактиране | редактиране на кода]

Интерфейсът се имплементира от клас или като разширение към друг интерфейс по същия начин, по който произлиза клас от друг клас с помощта на ":" нотация.

Неявна имплементация

Когато интерфейс се имплементира неявно, неговите компоненти трябва да бъдат public.

public class Adder : IBinaryOperation
{
    public double A { get; set; }
    public double B { get; set; }

    public double GetResult()
    {
        return A + B;
    }
}

public class Multiplier : IBinaryOperation
{
    public double A { get; set; }
    public double B { get; set; }

    public double GetResult()
    {
        return A*B;
    }
}

Начин на използване:

IBinaryOperation op = null;
double result;

// Adder имплементира интерфейса IBinaryOperation.

op = new Adder();
op.A = 2;
op.B = 3;

result = op.GetResult(); // 5

// Multiplier също имплементира този интерфейс.

op = new Multiplier();
op.A = 5;
op.B = 4;

result = op.GetResult(); // 20

Явна имплементация

По този начин може да се имплементират едновременно няколко интерфейса, които съдържат методи с еднаква сигнатура. Може също така компонентите да се имплементират явно. Компонентите на интерфейс, които са имплементирани явно от клас са достъпни само, когато обектът се използва като интерфейс.

public class Adder : IBinaryOperation
{
    double IBinaryOperation.A { get; set; }
    double IBinaryOperation.B { get; set; }

    double IBinaryOperation.GetResult()
    {
        return ((IBinaryOperation)this).A + ((IBinaryOperation)this).B;
    }
}

Начин на използване:

Adder add = new Adder();

// Тези компоненти не са достъпни
// add.A = 2;
// add.B = 3;
// double result = add.GetResult();

// Преобразуване на типа на интерфейса, за да има достъп до тях
IBinaryOperation add2 = add;
add2.A = 2;
add2.B = 3;

double result = add2.GetResult();

Забележка: Свойствата в класа, който обхваща IBinaryOperation са автоматично имплементирани от компилатора и полето е автоматично добавено.

Разширяване от множество интерфейси

Интерфейсите и класовете позволяват да се разширяват от множество интерфейси.

class MyClass : IInterfaceA, IInterfaceB
{
    ...
}

Това е интерфейс, който е разширен от два интерфейса.

interface IInterfaceC : IInterfaceA, IInterfaceB
{
    ...
}

Интерфейси и Абстрактни класове[редактиране | редактиране на кода]

Интерфейсите и абстрактните класове са подобни. Следва описание на някои важни разлики:

  • Абстрактният клас може да има член-променливи, както и неабстрактни методи или свойства. Интерфейсът не може.
  • Kласът или абстрактният клас може да наследява един клас или абстрактен клас.
  • Класът или абстрактният клас може да имплементира един или повече интерфейси.
  • Интерфейсът може само да разшири други интерфейси.
  • Абстрактният клас може да има непублични методи и свойства (също и абстрактни такива). Интерфейсът може да има само публични компоненти.
  • Абстрактният клас може да има константи, статични методи и статични компоненти. Интерфейсът не може.
  • Абстрактният клас може да има конструктори. Интерфейсът не може.

Шаблонни класове[редактиране | редактиране на кода]

Шаблонните класове(Generics) се добавят във версия 2.0 на .NET Framework. Концепцията на шаблонните класове позволява създаването на методи и класове в които не се конкретизира типа данни.

Използване на шаблонните класове (Generics)[редактиране | редактиране на кода]

Декларирането на шаблонен тип от класа Generics става със задаване на типа данни в счупените скоби (< >).

Пример :

  List<T> randomList= new List<T>;

Където Т е типа данни който използвате.

Класове[редактиране | редактиране на кода]

List<T>[редактиране | редактиране на кода]

Списъкът (List<T>)е динамичен масив, които няма фиксиран размер и позволява директен достъп по индекс на елементите му.

Деклариране

class SampleClass
{
    static void Main()
	{
		List<int> demo= new List<int>();
	}
}

Добавяне на елементи

class SampleClass
{
    static void Main()
    {
        List<int> demo = new List<int>();
        demo.Add(8);

    }
}

Премахване на елементи

class SampleClass
{
    static void Main()
    {
        List<int> demo = new List<int>();
        demo.Remove(8);
    }
}

Сортиране на елементи

class SampleClass
{
    static void Main()
	{
        List<int> demo = new List<int>();
		demo.Sort();

	}
}

LinkedList<T>[редактиране | редактиране на кода]

Свързаните списъци (LinkedList<T>) представляват колекция от списъци свързани по между си. Свързаните списъци ни дават възможност да поставяме елементи на желани от нас позиции.

Деклариране

class SampleClass
{
    static void Main()
    {
        LinkedList<int> demo = new LinkedList<int>();

    }
}

Добавяне на елементи

class SampleClass
{
    static void Main()
    {
        LinkedList<int> demo = new LinkedList<int>();
        demo.AddFirst(8);
        demo.AddLast(12);
        demo.AddAfter(linkedList.First, 5);
        demo.addBefore(linkedList.Last, 3);
    }
}

Stack<T>[редактиране | редактиране на кода]

Стекът (Stack<T>) представлява структура от данни от тип „последният влязъл първи излиза“. При структурите от данни от тип Stack<T> липсва индексатор и затова имаме достъп само до последния елемент, който е влязъл.

Деклариране

class SampleClass
{
    static void Main()
    {
        Stack<int> demo = new Stack<int>();

    }
}

Добавяне на елементи

class SampleClass
{
    static void Main()
    {
        Stack<int> demo = new Stack<int>();
        demo.Push(8);
        demo.Push(4);
        demo.Push(12);

    }
}

Премахване на елементи

class SampleClass
{
    static void Main()
    {
        Stack<int> demo = new Stack<int>();
        demo.Pop();
    }
}

Queue<T>[редактиране | редактиране на кода]

Опашката (Queue<T>) представлява структура от данни от тип „първият влязъл първи излиза“. При структурите от данни от тип Queue<T> липсва индексатор и затова имаме достъп само до първия елемент, който е влязъл.

Деклариране

class SampleClass
{
    static void Main()
    {
        Queue<int> demo = new Queue<int>();

    }
}

Добавяне на елементи

class SampleClass
{
    static void Main()
    {
        Queue<int> demo = new Queue<int>();
        demo.Enqueue(8);
        demo.Enqueue(4);
        demo.Enqueue(12);
    }
}
<syntaxhighlight lang="text">
'''Премахване на елементи'''
<syntaxhighlight lang="CSharp">
class SampleClass
{
    static void Main()
    {
        Queue<int> demo = new Queue<int>();
        demo.Dequeue();
    }
}

Интерфейс на класа Generics[редактиране | редактиране на кода]

Често е полезно използването на интерфейс за класа Generics. Например ако използваме IComparable<T> вместо IComparable избягваме ненужни операции с типовете данни.

В .NET Framework има няколко вградени интерфейса :
  • ICollection<T>
  • IComparer<T>
  • IEnumerable<T>
  • IEnumerator<T>
  • IList<T>
  • ISet<T>

Методи на класа Generics[редактиране | редактиране на кода]

Метод представител на класа Generic можем на да наречем метод който е деклариран с тип Т.

Пример :

 static void Swap<T>(ref T one, ref T two)
{
    T temporerry;
    temporerry  = one;
    one = two;
    two = temporerry;
}

public static void TestSwap()
{
    int a = 1;
    int b = 2;

    Swap<int>(ref a, ref b);// Извикваме метода с int като аргумент
    System.Console.WriteLine(a + " " + b);
}

Типът Т (Type-parameter)[редактиране | редактиране на кода]

Типовете данни Т (Type-parameters)са имена които заменят конкретен тип данни при създаването на нов обект от класа Generics. Те могат да бъдат обвързани с класове, или методи чрез поставяне на типа Т в счупените скоби < >. След декларацията на класа, или метода можем да извикваме и да подадем конкретен тип данни.

Where T:struct
типовете данни трябва да бъдат целочислени
where Т:class
типовете данни трябва да бъдат референтни
where T:new()
типовете данни трябва да имат конструктор които не приема параметри
where Т:<base_class>
типът данни трябва да бъде наследник на <base_class>
where T:<interface>
типът данни трябва да имплементира този интерфейс

Енумератори[редактиране | редактиране на кода]

Енумераторът представлява итератор. Енумераторите се използват чрез извикване на метода GetEnumerator() на обект който имплементира IEnumerable интерфейс.

Примера показва проста употреба на итератори в С#.

 	IEnumerator<MyType> iter = list.GetEnumerator();
            while (iter.MoveNext())
            {
                Console.WriteLine(iter.Current);
            }

Функционалност на генератор[редактиране | редактиране на кода]

.NET 2.0 Framework позволява на C# да представи итератор който има функционалността на генератор чрез конструкцията yield return.

 		// Метода взима итератор като вход

            public static IEnumerable<int> GetEven(IEnumerable<int> numbers)
            {
                foreach (int i in numbers)
                {
                    if (i%2 == 0)
                    {
                        yield return i;
                    }
                }
            }

            static void Main()
            {
                  int[] numbers = { 1, 2, 3, 4, 5, 6};
                  foreach (int i in GetEven(numbers))
                  {
                    Console.WriteLine(i); //изход 2, 4 and 6
                  }
            }

LINQ[редактиране | редактиране на кода]

LINQ съкратено от Language Integrated Queries е част от .NET Framework и опростява боравенето с данни.LINQ позволява писането на заявки към масиви, колекции и бази данни.

Синтаксис[редактиране | редактиране на кода]

LINQ е представен в C# 3.0 и позволява писането на заявки в C# по подбие на SQL.

var list = new List<int>{ 2, 7, 1, 3, 9 };

var result = from i in list
               where i > 1
               select i;

Разликата от SQL е мястото на ключовата дума from. В SQL е на последно място, а в C# е на първо. Това е така заради синтаксиса на C#.

Анонимни методи[редактиране | редактиране на кода]

Анонимните методи, или в настоящата си форма по-често цитирани като „ламбда изрази“, са функционалност, която позволява да се пишат функции, подобни на затварящи, директно вградени в кода.

Съществуват различни начини за създаване на анонимни методи. Преди въвеждането на C# 3.0 има ограничена поддръжка, чрез използването на делегати.

Виж също

Анонимни делегати[редактиране | редактиране на кода]

Това е функционалност на C# 2.0.

Анонимни делегати са указатели към функции, които съдържат анонимни методи. Целта е да се улесни използването на делегати чрез опростяване процеса на възлагане на функцията. Вместо да създава отделен метод, програмистът, използвайки правилния синтаксис, може да напише функцията директно в кода си, а компилаторът ще генерира анонимна функция за него.

Func<int, int> f = delegate(int x) { return x*2; };

Ламбда изрази[редактиране | редактиране на кода]

Това е функционалност на C# 3.0.

Ламда изразите предоставят прост синтаксис за вградени в кода функции, подобни на затварящите функции. Функциите с параметри подсказват типа на параметъра, освен ако изрично не е посочено друго.

// [Аргументи] => [тяло на метода]

// С въведени параметри
n => n == 2
(a, b) => a + b
(a, b) =>{ a++; return a + b; }

// С изрично посочени параметри
(int a, int b) => a + b

// Без въведени параметри
() => return 0

// Присвояване на ламбда израз на делегат
Func<int, int, int> f = (a, b) => a + b;

Телата на ламбда изрази, съдържащи повече от един израз, са оградени от скоби. Вътре в скобите, кода може да бъде написан като стандартен метод.

(a, b) =>{ a++; return a + b; }

Ламбда изразите могат да бъдат подавани и като аргументи при извикването на метод, подобно на анонимни делегати, но с по-естетичен синтаксис.

var list = stringList.Where(n => n.Length > 2);

Ламбда изразите по същество са методи, генерирани от компилатора, които се подават чрез делегати. Тези методи са запазени само за компилатора и не могат да се използват в друг контекст.

Допълващи методи[редактиране | редактиране на кода]

Това е функционалност на C# 3.0.

Допълващите (разширяващи) методи са форма на т. нар. „синтактична захар“. Те създават илюзията за добавяне на нови методи към съществуващ клас извън неговото дефиниране. На практика, допълващият метод е статичен метод, който може да се извика по същия начин, както и инстантния метод. Извикващия метод е обвързан с първия параметър на метода, обозначен от ключовата дума

this

.

public static class StringExtensions
{
    public static string Left(this string s, int n)
    {
        return s.Substring(0, n);
    }
}

string s = "foo";
s.Left(3); // същото като StringExtensions.Left(s, 3);

Виж също

Други[редактиране | редактиране на кода]

Затварящи блокове[редактиране | редактиране на кода]

C# изпълнява затварящи блокове чрез използването на

using

конструкцията.

Using

конструкцията приема израз, чийто резултат е обект, включващ IDisposable. Компилаторът генерира код, който гарантира затварянето (унищожаването) на обекта, когато кода, в обхвата на

using

конструкцията, бъде изпълнен. Използването на

using

конструкцията е „синтактична захар“. Тя прави кода по-четим от еквивалентния му блок

try ... finally

.

public void Foo()
{
    using (var bar = File.Open("Foo.txt"))
    {
        // do some work
        throw new Exception();
        // bar will still get properly disposed.
    }
}

Синхронизация на нишки[редактиране | редактиране на кода]

Lock

конструкцията в C# е още един пример за ползите от „синтактичната захар“. Той действа чрез маркирането на блок от кода като критична зона като взаимно изключва достъпа до посочения обект. Подобно на

using

конструкцията,

lock

работи като компилатора генерира

try ... finally

блок на негово място.

private static StreamWriter _writer;

public void ConcurrentMethod()
{
    lock (_writer)
    {
        _writer.WriteLine("Line 1.");
        _writer.WriteLine("Followed by line 2.");
    }
}

Атрибути[редактиране | редактиране на кода]

Атрибути (характеристики) са парчета информация, която се съхранява като метаданни в компилирания пакет. Атрибутът може да бъде добавен към видове и участници (напр. свойства, методи). Атрибутите могат да се използват за по-добро управление на пред-процесорните инструкции.

[CompilerGenerated]
public class $AnonymousType$120
{
    [CompilerGenerated]
    public string Name { get; set; }
}

.NET Framework идва с предварително зададени атрибути, които могат да бъдат използвани. Някои от тях играят важна роля по време на изпълнението на програмата, докато други са просто синтактична украса в кода (напр.

CompilerGenerated

 – само маркира, че е елемент, генериран от компилатора). Също така могат да бъдат създадени и атрибути, дефинирани от самия програмист. Атрибутът по същество е клас, който наследява своите свойства от класа

System.Attribute

. По правило, класовете атрибути завършват с „Attribute“ в техните имена, но това не се изисква при тяхното използване.

public class EdibleAttribute : Attribute
{
    public EdibleAttribute() : base()
    {

    }

    public EdibleAttribute(bool isNotPoisonous)
    {
        this.IsPoisonous = !isNotPoisonous;
    }

    public bool IsPoisonous { get; set; }
}

Показване на използвания атрибут чрез използването на допълнителни constructor параметри.

[Edible(true)]
public class Peach : Fruit
{
   // Членове, ако има такива
}

Пред-процесор[редактиране | редактиране на кода]

C# поддържа „пред-процесорни инструкции“[4] (макар че той не разполага с действителен пред-процесор), базирани на пред-процесора на C, което позволява на програмистите да определят символи, но не и макроси. Включени са и условни изрази като

#if

,

#endif

, и

#else

. Инструкции от типа на

#region

дават насоки на редакторите на кода за т.нар „свиване на кода“ (code folding). Блокът, отворен с

#region

трябва да бъде затворен с

#endregion

.

Коментиране на кода[редактиране | редактиране на кода]

C# използва двойна наклонена черта (

//

) за да покаже, че останалата част от реда е коментар.

public class Foo
{
    // Коментар
    public static void Bar(int firstParam) {}  // Също коментар
}

Многоредовите коментари могат да бъдат означени с отварящи наклонена черта и звездичка (

/*

) и затварящи звездичка и наклонена черта (

*/

).

public class Foo
{
    /* Многоредов
       коментар  */
    public static void Bar(int firstParam) {}
}

Коментарите не се вграждат един в друг. Това са два отделни коментара.

// Може да се изпише /* */ */ */ /* /*
/* Може да се изпише /* /* /* но завършва с */

Коментарите на един ред, започващи с три наклонени черти се използват за XML документиране. Трябва да се има предвид, че това е конвенция, която се използва от Microsoft Visual Studio и не е част от езика C#.

    /// <summary>
    /// Този клас е много класен.
    /// </summary>

Система за документиране в XML[редактиране | редактиране на кода]

Системата за документиране, използвана в C#, е подобна на Javadoc, използвана в Java, но за разлика от нея е базирана на XML. Към момента има два метода на документиране, които се поддържат от компилатора на C#.

Подобно на често срещаните в генерирания от Visual Studio код, едноредовите коментари, използвани за документиране, се представят на ред, започващ с

// /

.

public class Foo
{
    // / <summary>Кратко описание на метода</summary>
    // / <param name="firstParam">Описание на параметъра</param>
    // / <remarks>Забележки относно метода/remarks>
    public static void Bar(int firstParam) {}
}

Многоредовите коментари, използвани за документиране, са дефинирани в спецификациите на версия 1.0 на C#.[5] Въпреки това, те не са поддържани до пускането на .NET 1.1. Тези коментари са обозначени с отварящи наклонена черта, звездичка, звездичка (

/**

) и затварящи звездичка, наклонена черта (

*/

).[6]

public class Foo
{
    /** <summary>Кратко описание на метода</summary>
     *  <param name="firstParam">Описание на параметъра.</param>
     *  <remarks>Забележки относно метода</remarks> */
    public static void Bar(int firstParam) {}
}

Трябва да се има предвид, че има някои строги критерии по отношение на празно пространство (space) и XML документацията, когато се използва метода наклонена черта, звездичка, звездичка (

/**

).

Този блок код:

/**
 * <summary>
 * Кратко описание на метода.</summary>*/

създава различен XML коментар от този блок код:[6]

/**
 * <summary>
   Кратко описание на метода. </Обобщение>*/

Синтаксисът за коментари, използвани за документиране, и тяхното XML представяне са определени в незадължително приложение към C# стандарта на ECMA. Същия стандарт определя и правила за обработка на такива коментари, и тяхната трансформация в обикновен XML документ с точни правила за съответствие и връзка на идентификаторите в Common Language Infrastructure (CLI) към съответстващите елементи в документацията. Това позволява на всяка интегрирана среда за разработка (IDE) на C# или на друг инструмент за разработка, да намери документация за всеки символ в кода по точно определени начин.

C# 5.0 характеристики[редактиране | редактиране на кода]

С обявяването на CLR (Common Language Runtime) 4.5 е публикувана и нова версия на C# – 5.0. Диаграмата показва развитието на C# и основните характеристики, които са се появили през годините.

C# еволюция
Версия IDE Нови характеристики
C# 1.0 VS2002 Managed code -
C# 2.0 VS2005 Generics Anonymous Methods Nullable Types -
C# 3.0 VS2008 Lambda Expressions Extension Methods Expression Tree Anonymous Types LINQ Implicit Typing (var)
C# 4.0 VS2010 Late Binding (dynamic) Named Arguments Oprional Parameters More COM Support -
C# 5.0 VS2011 Async Feature Caller Information -

В C# 5.0 има две нови характеристики – Asynchronous Programming (Асинхронно Програмиране) и Caller Information.

Async свойство[редактиране | редактиране на кода]

Двете ключови думи, които се използват при асинхронно програмиране са: модификаторът async и операторът await. Метод включващ модификаторът async се нарича асинхронен метод. Тези нови характеристики улесняват асинхронно програмиране. Например, при програмиране на Winform, UI нишката ще бъде блокирана докато се използва HttpWebRequest. От гледна точна на потребителя, не може да се взаимодейства с форма преди заявката да е изпълнена.

  private void
   btnTest_Click(object sender, EventArgs e) 
   { 
            var request = WebRequest.Create(txtUrl.Text.Trim()); 
            var content=new MemoryStream(); 
          using (var response = request.GetResponse()) 
         { 
          using (var responseStream = response.GetResponseStream()) 
          { 
           responseStream.CopyTo(content); 
           } 
         } 
    txtResult.Text = content.Length.ToString(); 
   }

Примерът показва, че след като натиснем Test бутона, не можем да направим никакви промени във формичката, преди резултата да се появи в текстовото поле.

Използването на метода BeginGetResponse е подобно на async медот, но е по-сложно.

Използване на async метод:

private async void
btnTest_Click(object sender, EventArgs e) 
{ 
	var request = WebRequest.Create(txtUrl.Text.Trim()); 
	var content = new MemoryStream(); 
	Task<WebResponse> responseTask = request.GetResponseAsync(); 

	using (var response = await responseTask) 
	{
		using (var responseStream = response.GetResponseStream()) 
 		{ 
			Task copyTask = responseStream.CopyToAsync(content); 
         //Операторът await се прилага върху върнатата задача. Той спира изпълнението на Метода,
         //докато задачата е завършена. В същото време, контролът се връща на повикващия на отложения метод.
			await copyTask; 
 } 
	} 
	txtResult.Text = content.Length.ToString(); 
}

Caller Information[редактиране | редактиране на кода]

Caller Information ни помага при проследяването, отстраняване на грешки и създаване на инструменти за диагностициране. Помага за избягване на дублиращи кодове, които обикновено се прилагат в много методи за същата цел, като за логване и проследяване.

Следващия пример илюстрира използването на Caller Information:

namespace CSharp.Syntax.CallerInformation {

    using System;

    class Program {
        static void Main(string[] args) {
            InsertLog("Main");
            MethodB();
            Console.ReadLine();
        }

        static void MethodA() {
            InsertLog("MethodA");
            MethodB();
        }

        static void MethodB() {
        }

        static void InsertLog(string methodName) {
            Console.WriteLine("{0} called methodB at {1}",
                              methodName,
                              DateTime.Now);
        }
    }
}

И в двата метода Main и MethodA, методът InsertLog се ползва за логване.

Можем да променим кода по следния начин:

namespace CSharp.Synax.CallerInformation {

    using System;

    class Program {
        static void Main(string[] args) {
            //InsertLog("Main"); 
            MethodB();
            Console.ReadLine();
        }

        static void MethodA() {
            //InsertLog("MethodA"); 
            MethodB();
        }

        static void MethodB
        (
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "",
            [CallerLineNumber] int sourceLineNumber = 0
        )
        {
            InsertLog(memberName);
        }

        static void InsertLog(string methodName) {
            Console.WriteLine("{0} called method B at {1}",
                              methodName,
                              DateTime.Now);
        }
    }
}

Диалекти[редактиране | редактиране на кода]

Spec#[редактиране | редактиране на кода]

Spec# е система за програмиране, която представлява разширение на езика C#. Повлияна е от JML, AsmL, and Eiffel. Spec# включва структури като non-null types, preconditions, postconditions, и object invariants. Spec # позволява стабилна методология за програмиране, която позволява специфицирането и аргументирането на обектни инварианти дори в присъствието на callbacks и multi-threading Spec# е опит за по-ефективно разработване и поддръжката на висококачествен софтуер. Spec# се произнася „спец шарп“ и може да се пише и търси както Spec#, така и Specsharp. Spec# е разработен от Microsoft Research.

    static void Main(string![] args)
        requires args.Length > 0
    {
        foreach(string arg in args)
        {

        }
    }

Non-nullable типове[редактиране | редактиране на кода]

Една от най-често срещаните грешки в обектно-ориентирани програмине е причинена от разликата в null reference. За да се избегне тази грешка Spec# прави разлика между non-null типове и вероятните null типове. Non-null типове проверяват дали променливите от null тип не са null тип. Ако са null ще бъде хвърлен exception

   string! input

In use:

   public Test(string! input)
   {
      ...
   }

Предусловия[редактиране | редактиране на кода]

Preconditions (предусловията) се проверяват преди методът да бъде изпълнен

   public Test(int i)
      requires i > 0;
   {
      this.i = i;
   }

Следусловия[редактиране | редактиране на кода]

Postconditions (следусловията) се проверяват след като методът бъде изпълнен

   public void Increment()
      ensures i > 0;
   {
      i++;
   }

Проверка на изключения[редактиране | редактиране на кода]

Spec# добавя проверени изключения като тези в Java

    public void DoSomething()
        throws SomeException; // SomeException : ICheckedException
    {

        ...

    }

Източници[редактиране | редактиране на кода]

  1. C# Reference // Microsoft. Посетен на 14 август 2013. (на английски)
  2. Schildt, Herbert. C# 4.0 The Complete Reference. McGraw Hill Professional, 2010. ISBN 978-0-07-174117-0. p. 976. (на английски)
  3. а б Archer, Part 2, Chapter 4:The Type System
  4. C# Preprocessor Directives // C# Language Reference. Microsoft. Посетен на 18 юни 2009.
  5. Horton, Anson. C# XML documentation comments FAQ // 11 септември 2006. Посетен на 11 декември 2007.
  6. а б Delimiters for Documentation Tags // C# Programmer's Reference. Microsoft, 1 януари 1970 GMT. Посетен на 18 юни 2009.

Литература[редактиране | редактиране на кода]

Външни препратки[редактиране | редактиране на кода]