Академический Документы
Профессиональный Документы
Культура Документы
2/69
Основы C#.
3/69
Основы C#.
4/69
Основы C#.
В появившемся окне New Project слева выбираем, естественно, Visual C#, а справа тип
приложения - Console Application:
В качестве имени проекта (Name) напечатайте first или что-то в этом роде. Нажмите на
кнопку для закрытия данного диалогового окна.
Теперь приступаем к коду. Наша первая программа просто выведет некоторое
фиксированное слово в консольное окошко. Вот ее листинг.
using System;
namespace first
{
///
/// Summary description for Class1.
///
class Class1
{
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
Console.WriteLine("Привет из C#");
}
}
}
Запускаем программу, нажав Ctrl+F5. Результат будет таким:
5/69
Основы C#.
...
using System;
...
то мы можем вместо длинных имен использовать более короткие. В частности, вместо
System. Console можно писать просто Console. Что мы делаем в строчке
...
Console.WriteLine("Привет из C#");
...
Далее мы в нашей программе объявляем класс Class1. Что такое классы мы посмотрим в
последующих уроках, сейчас же достаточно сказать, что в C# не существует глобальных
функций, так что нам ничего не остается, как завести сначала класс и затем функцию Main
в нем (функция Main обязательно должна быть в каждой программе на C#, и именно с
этой функции и начинается выполнение программы. Обратите также внимание, что эта
функция пишется с прописной (большой) буквы. C# различает строчные и прописные
буквы, так что это важно). Кроме того, эта функция объявлена с модификатором static.
Это означает, что она не относится к конкретному экземпляру класса Class1, а
принадлежит всему классу. В нашей функции Main мы просто выводим на экран
некоторую строчку методом WriteLine.
С первой программой на C# все.
6/69
Основы C#.
в начале программы (хотя, впрочем, она там скорей всего и так есть).
Таким образом следующие три объявления переменной k равносильны:
int k;
using System;
...
Int32 k;
и
System.Int32 k;
Разумеется, аналогично мы имеем дело и другими типами языка C#.
Объявление переменной можно совместить с инициализацией (заданием начального
значения):
int z=88;
7/69
Основы C#.
8/69
Основы C#.
Отметим, что в C#, в отличии от многих других языков программирования, нельзя вместо
false использовать 0, а вместо true - любое ненулевое число. Так, следующий фрагмент
содержит ошибку:
int k;
...
if(k) //Ошибка!
...
9/69
Основы C#.
Урок 6. Массивы в C#
Массивы в C# несколько отличаются от других C-подобных языков. Начнем сразу с
примеров. Пример первый:
...
int[] k; //k - массив
k=new int [3]; //Определяем массив из 3-х целых
k[0]=-5; k[1]=4; k[2]=55; //Задаем элементы массива
//Выводим третий элемент массива
Console.WriteLine(k[2].ToString());
...
Во-вторых, так как массив представляет из себя ссылочный объект, то для создания
массива необходима строка
k=new int [3];
Именно в ней мы и определяем размер массива. Хотя, вообще говоря, возможны
конструкции вида
int[] k = new int [3];
Обратите внимание, что пара квадратных скобок только одна. Естественно, что в нашем
примере у массива 6 (=2*3) элементов (k[0,0] - первый, k[1,2] - последний).
Аналогично мы можем задавать многомерные массивы. Вот пример трехмерного массива:
int[,,] k = new int [10,10,10];
А вот так можно сразу инициализировать многомерные массивы:
int[,] k = {{2,-2},{3,-22},{0,4}};
10/69
Основы C#.
11/69
Основы C#.
12/69
Основы C#.
Console.WriteLine("Совсем неудовлетворительно");
//Ошибка! Тут пропушен break
case 2:
Console.WriteLine("Неудовлетворительно");
break;
...
...
default:
Console.WriteLine("...");
//Ошибка! Тут пропушен break
}
13/69
Основы C#.
В этом примере начальное значение для счетчика цикла равно нулю, и в условии
продолжения цикла мы пишем знак "меньше", после которого ставится количество
элементов в массиве. Разумеется, если в цикле должен выполнится только один оператор,
то фигурные скобки можно не писать. Тут все, как в других C/C++-подобных языках.
Теперь рассмотрим пример цикла foreach:
int[] m = {-5, 4, 10};
int sum=0;
foreach(int i in m){
sum+=i;
}
В данном примере мы суммируем все элементы массива m, записывая сумму в sum.
В приведенном примере наш цикл перебирает все элементы массива m. На это нам
указывает строка
...
foreach(int i in m){
...
которая интерпретируется так: для каждого целого числа из массива m делам что-то там.
Если бы элементами массива были бы не целые, а, скажем, вещественные, то мы записали
бы что-то вроде:
...
foreach(float i in m){
...
Т. е. мы пишем именно тип элементов массива. На самом деле foreach используется не
только для массивов, но и для других объектов (например, для хэш-таблиц). Но это будет
рассмотрено в последующих уроках.
14/69
Основы C#.
15/69
Основы C#.
class Test
{
[STAThread]
static void Main(string[] args)
{
Worker wrk1 = new Worker();
wrk1.age=34;
wrk1.name="Иванов";
Console.WriteLine(wrk1.name+", "+wrk1.age);
}
}
}
Запустите программу. Она, как и следовало ожидать, выведет на экран "Иванов, 34".
Давайте кратко обсудим наш код. Во-первых, в строчках
...
class Worker
{
public int age=0;
public string name;
}...
мы определили наш класс Worker. Внутри этого класса существует две переменные -
целая age для возраста и name строкового типа для имени. Обратите внимание, что, в
отличие от C/C++, мы можем задавать некоторое начальное значение прямо сразу после
объявления переменной:
16/69
Основы C#.
...
public int age=0;
...
Начальное значение задавать вовсе не обязательно - это видно по переменной name.
Перед переменными мы пишем ключевое слово public. Значение у него такое же, как и в
C++ - а именно это означает, что эта переменная (или функция) будет видна вне класса.
Если мы не напишем перед переменной никакого модификатора доступа, или напишем
private, то переменная не будет видна снаружи класса и ее смогут использовать только
функции этого же класса (т. е. она будет для "внутреннего использования").
Далее в строчке
...
Worker wrk1 = new Worker();
...
17/69
Основы C#.
18/69
Основы C#.
19/69
Основы C#.
...
class Boss : Worker{
...
Такая запись и означает, что новый класс Boss будет потомком другого класса (Worker в
данном случае). Так как наш класс - потомок другого класса, то он автоматически умеет
все тоже самое, что и родительский класс. В частности, в нем есть переменная age для
хранения возраста. Далее, в класс Boss мы вводим переменную numOfWorkers для
хранения количество подчиненных у нашего босса. В родительском классе такой
переменной не было. Кроме того, нас не устроила реализация метода setAge из
родительского класса Worker (по каким-то там внутренним инструкциям фирмы возраст
босса не может превышать 45 лет, тогда как возраст работника не может превышать 100),
поэтому мы просто написали в классе Boss метод с таким же именем. Обратите внимание
на слово new при объявлении метода:
...
public new void setAge(int age)
...
20/69
Основы C#.
21/69
Основы C#.
Здесь класс ClassB объявлен внутри класса ClassA. Объявлен он со словом private, так что
его экземпляры мы можем создавать только внутри класса ClassA (что мы и делаем в
конструкторе класса ClassA). Методы класса ClassA имеют доступ к экземпляру класса
ClassB (как, например, метод SomeMethod).
Вложенный класс имеет смысл использовать тогда, когда его экземпляр используется
только в определенном классе. Кроме того, при использовании вложенных классов
улучшается читаемость кода - если нас не интересует устройство основного класса, то
разбирать работу вложенного класса нет необходимости.
Обратите также внимание, как вложенные классы показываются на вкладке ClassView:
22/69
Основы C#.
Здесь Vector - это введенный нами класс. Если мы не сделаем перегрузку операторов, то
мы не сможем использовать знак "плюс" для экземпляров нашего класса Vector. Вот как
это можно сделать для бинарного (т. е. имеющего два параметра) оператора:
using System;
namespace test
{
class Vector
{
float x, y; //Координаты
//Конструктор
public Vector(float x, float y)
{
this.x=x;
this.y=y;
}
//Чтение x-координаты
public float GetX()
{
return x;
}
//Чтение y-координаты
public float GetY()
{
return y;
}
//Перегрузка оператора +
public static Vector operator +(Vector w, Vector v)
{
Vector res = new Vector(w.x + v.x, w.y + v.y);
return res;
}
}
class Test
{
static void Main(string[] args)
{
Vector a=new Vector(1, 2), b=new Vector(2, -1), c;
c = a + b;
Console.WriteLine("x=" + c.GetX() + ", "
+ "y=" + c.GetY());
}
}
}
Код для нашего класса приведен полностью, хотя перегрузка оператора занимает всего
несколько строк. Вот они:
...
public static Vector operator +(Vector w, Vector v)
{
Vector res = new Vector(w.x + v.x, w.y + v.y);
return res;
}
23/69
Основы C#.
...
Синтаксис здесь такой - если мы перегружаем оператор op, то мы должны в качестве
имени оператора написать operator op (в нашем случае мы перегружаем +, поэтому пишем
operator +). Далее, раз это метод, то мы пишем в круглых скобках его параметры. Для
бинарного оператора их два (в отличие от C++, где параметр - это только правый операнд,
левый же передается неявно - через указатель this). У нас они оба имеют тип Vector. Перед
operator op пишем тип возвращаемого значения. Так как сумма двух векторов - это опять
вектор, то перед именем нашего метода пишем Vector. Модификатор доступа public
понятен - без него мы бы не смогли использовать перегруженный оператор вне класса. А
вот слово static важно - в C# перегруженные операторы идут как static. Не забывайте его
писать.
Внутри перегруженного оператора мы заводим переменную res типа Vector для возврата
через нее результата. Ее мы определяем через конструктор с двумя параметрами, который
мы ранее написали для нашего класса. Сложение двух векторов происходит так, как и
должно происходить – по координатно.
В классе test мы испытываем наш перегруженный оператор. Все должно работать как и
положено, и результат сложения должен вывестись на консоль в виде "x=3, y=1".
24/69
Основы C#.
Обратите внимание, что при перегрузке унарных операторов (как, впрочем, и при
перегрузке бинарных), мы должны поставить модификатор static.
Для испытания перегруженного оператора изменить класс Test приблизительно
следующим образом:
class Test
{
static void Main(string[] args)
{
Vector a=new Vector(1, 2), b;
b = -a;
Console.WriteLine("x=" + b.GetX() + ", "
+ "y=" + b.GetY());
}
}
25/69
Основы C#.
26/69
Основы C#.
27/69
Основы C#.
Обратите внимание на то, что из какого конкретно класса вызывается функция (из
родительского или производного) определяется на этапе выполнения программы, а не на
этапе компиляции. В принципе в переменную родительского типа мы могли бы записать
экземпляр именно родительского класса. В этом случае, естественно, вызвалась бы
функция родительского класса. Вот поясняющий это утверждение пример:
class Test
{
static void Main(string[] args)
{
Worker boss;
bool b;
//Присваиваем значение в переменную b
...
if(b)
{
//В переменной boss - экземпляр класса Boss
boss=new Boss();
}
else
{
//В переменной boss - экземпляр класса Worker
boss=new Worker();
}
//Вызываем метод класса Boss или Worker
boss.setAge(50);
Console.WriteLine("Возраст "+boss.getAge());
}
}
С этим уроком все!
28/69
Основы C#.
29/69
Основы C#.
30/69
Основы C#.
31/69
Основы C#.
32/69
Основы C#.
33/69
Основы C#.
34/69
Основы C#.
35/69
Основы C#.
{
System.Console.WriteLine(MyClass.GetSomeNumber()); //Выведется 2
System.Console.WriteLine(MyClass.GetSomeNumber()); //Выведется 3
}
}
Указанный фрагмент выведет 2 и 3.
36/69
Основы C#.
Синтаксис у нее такой - сначала пишем ключевое слово params, затем - тип параметров
(int в данном случае), после которого ставим пустые квадратные скобки и в конце этой
конструкции пишем произвольное имя параметра (в нашем примере он назван args).
В классе test мы испытываем наш класс MyClass, а именно, вызываем функцию Sum с
разным числом параметров. Все работает как надо и наша программа покажет на экране 4,
12 и 10.
37/69
Основы C#.
Equals. Метод, возвращает true, если строки равны, false - если не равны. Может быть как
статическим, так и не статическим. Пример использования:
String s1="qqq", s2="www";
System.Console.WriteLine(String.Equals(s1, s2).ToString());
//Статический метод
System.Console.WriteLine(System.Console.WriteLine(String.Eq
uals(s1, s2).ToString());
//Не статический метод
System.Console.WriteLine(s1.Equals(s2).ToString());.ToString());
Метод Substring. Позволяет извлечь из строки подстроку. Пример использования:
String s1="abcdefg", s2;
s2=s1.Substring(3, 2);
System.Console.WriteLine(s2); //Напечатается "de"
Параметры тут такие: первый - с какого места извлекаем (нумерация с нуля) и второй -
сколько символов извлекаем.
Метод Insert. Вставляет в строку другую строку. Пример использования:
String s1="abcdefg", s2;
s2=s1.Insert(1, "xyz");
System.Console.WriteLine(s2); //Напечатается "axyzbcdefg"
Первый параметр тут - это куда вставляем (нумерация, как всегда, с нуля), второй - что за
строчку вставляем.
Метод IndexOf. Позволяет найти в строке подстроку. Пример использования:
String s1="abcabcab", s2="bc", s3="zzz";
System.Console.WriteLine(s1.IndexOf(s2)); //Напечатается 1
38/69
Основы C#.
System.Console.WriteLine(s1.IndexOf(s3)); //Напечатается -1
Этот метод возвращает номер позиции, на котором в строке находится передаваемая в
качестве параметра подстрока. Если такой подстроки нет, то возвращается -1.
Метод Replace. Производит замену в строке. Пример использования:
String s1="abcabcab", s2="bc", s3;
s3=s1.Replace(s2, "q");
System.Console.WriteLine(s3); //Напечатается aqaqab
39/69
Основы C#.
Метод Equals. Служит для сравнения двух строк. Возвращает true или false. Пример
использования:
if(s1.Equals(s2))
System.Console.WriteLine("Строки равны");
else
System.Console.WriteLine("Строки не равны");
Метод Insert. Вставляет символы в заданную позицию (Нумерация идет с нуля). Пример
использования:
StringBuilder s1=new StringBuilder("abcde");
s1.Insert(2, "xyz");
System.Console.WriteLine(s1); //Напечатается "abxyzcde"
Метод Remove. Удаляет символы из строки. Пример использования:
StringBuilder s1=new StringBuilder("abcde");
s1.Remove(1, 2);
System.Console.WriteLine(s1); //Напечатается "ade"
Первый параметр у Remove - это с какой позиции удаляем (нумерация с нуля), второй -
сколько символов удаляем.
Метод Replace. Заменяет символы. Пример использования:
StringBuilder s=new StringBuilder("abcdeabcde");
s.Replace("abc", "ZZZ");
System.Console.WriteLine(s); //Напечатается "ZZZdeZZZde"
40/69
Основы C#.
Параметр out аналогичен параметру ref, только при его использовании в функцию можно
передавать неинициализированные переменные (т. е. переменные с незаданными
начальными значениями). Вот пример:
class Test
{
static void SomeFunction2(ref int a)
{
a=55;
}
static void SomeFunction3(out int a)
{
a=66;
}
public static void Main()
{
int z=0; //Переменную z надо обязательно инициализировать
SomeFunction2(ref z);
Console.WriteLine(z); //Напечатается 55
int y; //Переменную y можно не инициализировать
SomeFunction3(out y);
Console.WriteLine(y); //Напечатается 66
}
}
При вызове такой функции обязательно использование ключевого слова out.
41/69
Основы C#.
42/69
Основы C#.
43/69
Основы C#.
Console.WriteLine(SomeClass.SomeFunc((float)10));
...
Если бы мы вызвали метод без слова float, то вызвался бы перегруженный вариант для
целых чисел.
44/69
Основы C#.
45/69
Основы C#.
46/69
Основы C#.
break;
}
//Печатаем результат.
int x=4;
Console.WriteLine("f({0})={1}", x, f(x));
}
}
}
Далее мы в нашем тестовом классе объявляем несколько методов - f1, f2 и f3. Все эти
методы имеют тип float и один параметр типа float (как и у делегата MyFunc). Функции
делают свой подсчет по-разному - первая просто возвращает параметр, вторая - квадрат
параметра, третья - корень из параметра.
Далее мы в методе Main создаем экземпляр нашего делегата и в зависимости от ответа
пользователя записываем в него f1, f2 или f3. Потом в строке
...
Console.WriteLine("f({0})={1}", x, f(x));
...
мы выводим значение делегата при некотором x (равном 4 в нашем примере). Вернее
сказать, мы выводим не значение делегата, а значение функции, которую мы записали в
делегат.
В зависимости от ответа пользователя в делегат запишется тот или иной вариант функции
и программа выведет сам x (4), x умножить на x (16) или корень из x (2):
47/69
Основы C#.
объявляем делегат. Параметров у него нет (хотя могут и быть), тип - void.
Затем мы объявляем класс MyEvent, внутри которого объявляем событие f (имя
произвольное):
...
public event EventHandler f;
...
48/69
Основы C#.
Синтаксис тут такой - модификатор доступа (у нас public), затем ключевое слово event,
потом имя делегата, на основании которого мы создаем наше событие (у нас это
EventHandler) и, наконец, имя произвольное событие (f). Обратите внимание, что событие
появится на вкладке ClassView:
Остается теперь указать, что за обработчик будет у события f класса MyEvent. Это мы
делаем в строке
...
w.f+=new EventHandler(z);
...
Этой строчкой мы указываем, что событие f будет обрабатывать метод z класса Test.
Если мы запустим наше программу, то на вызовется обработчик для события (т. е.
выведется надпись "Вызов обработчика"):
Обратите внимание, что обработчик для события мы пишем в тестовом классе (т. е. в
классе Test, в котором мы объявили экземпляр нашего класса с событиями). Так мы
делаем всегда, и не только на платформе .NET.
49/69
Основы C#.
При запуске нашей программы к классе Test мы создаем новый экземпляр класса MyDie
игральной кости, приписываем к событию max класса MyDie обработчик z, и
подкидываем кость 10 раз. Если выпадет шестерка, то возникнет событие max и
выполнится обработчик для него. Результат выполнения программы может быть,
например, таким:
50/69
Основы C#.
51/69
Основы C#.
52/69
Основы C#.
53/69
Основы C#.
Далее мы определяем для нашего атрибута внутреннюю переменную name типа string и
конструктор с параметром типа string. В конструкторе мы записываем значение в
переменную name. Еще чуть ниже мы пишем в классе атрибута свойство только для
чтения:
...
public virtual string Name
{
get
{
return name;
}
}
...
После создания класса атрибута мы применяем его к другому классу Test. Для этого мы
должны создать экземпляр атрибута TestAttribute непосредственно перед классом Test:
...
[TestAttribute("Igor Alexeev")]
class Test
{
...
54/69
Основы C#.
55/69
Основы C#.
Но для больших, настоявших программ это не самый лучший способ. Гораздо лучше
вынести метод Main в отдельный класс:
using System;
namespace test
{
class Test
{
//... (поля, методы, ... класса Test)
}
//Класс приложения для вызова метода Main.
class App
{
static void Main()
{
//Создаем экземпляр класса Test.
Test c = new Test();
//...
}
}
}
Вообще говоря в программе у вас, как правило, будет много классов. И лучше каждый из
них хранить в отельном файле (с расширением *.cs).
56/69
Основы C#.
double pi=3.1415926;
//Выведется 3.14
Console.WriteLine("pi={0:f2}", pi);
int b=255;
//Выведется FF.
Console.WriteLine("b={0:X}", b);
int c=255;
//Выведется ff.
Console.WriteLine("c={0:x}", c);
double d=1003.214;
//Выведется $1, 003.14 в английской версии Windows и
//1 003,14 р. в русской.
Console.WriteLine("d={0:c}", d);
double e=213.1;
//Выведется 2.131000e+002
Console.WriteLine("e={0:e}", e);
...
Параметры подстановочных знаков можно использовать как строчные, таки и прописные -
это все равно. Исключение - вывод числа в шестнадцатеричном виде (при использовании
h цифры a, ..., f будут строчными, при использовании H - прописными).
57/69
Основы C#.
58/69
Основы C#.
ob2=new System.Object();
//Выведется False.
Console.WriteLine(ob1.Equals(ob2));
//Выведется System.Object.
Console.WriteLine(ob1.GetType());
//Выведется System.Object.
Console.WriteLine(ob1.ToString());
Виртуальные методы класса System.Object часто переписывают в классах-потомках.
59/69
Основы C#.
60/69
Основы C#.
61/69
Основы C#.
62/69
Основы C#.
}
//Тестовый класс.
class App
{
static void Main()
{
SomeClass s=new SomeClass();
Console.WriteLine("a={0}, b={0}", s.a, s.b);
}
}
Здесь в классе SomeClass два конструктора. Второй конструктор (без параметров)
вызывает первый (передавая в него значения 1 и 1):
...
public SomeClass():this(1, 1)
...
63/69
Основы C#.
64/69
Основы C#.
65/69
Основы C#.
66/69
Основы C#.
67/69
Основы C#.
68/69
Основы C#.
Обратите внимание, что метод Move можно использовать не только для перемещения, но
и для переименования папки (что мы, фактически, в нашем примере и делаем). Для этого
папка, задаваемая первым параметром (т. е. та, которую перемещаем) должна находиться
на том же уровне, что и папка, задаваемая вторым параметром (т. е. куда перемещаем).
69/69