четверг, 10 июня 2010 г.

Эволюция языка C#

Прочитав посты из блога soumya, решил написать заметку по языку C#.

Каждые 2-3 года выходит новая версия .NET. В каждой версии придумывают новые технологии (WPF) и улучшают старые (WinForms, ASP.NET). Изменения эти значительны и требуют много времени на изучение. Синтаксис же языка C# меняется несильно.

Обычно пишут списком: что появилось в C# 2.0, 3.0 и т.д. Чтобы не копипастить материал с других сайтов, я сгруппирую его по языковым конструкциям. Примеры максимально простые, многие детали языка опущены.

Буду писать C# 1.0, при этом подразумевать реализацию этого языка в Visual Studio .NET 2003 (C# 2.0 – VS 2005, C# 3.0 - VS 2008, C# 4.0 - VS 2010).

Переменная

Именованная величина, которая может принимать различные значения.

Объявление переменной в C# 1.0:

int i = 0;

Для типов-значений в C# 2.0 можно использовать знак ‘?’:

int? i = null; //можем присвоить null

В C# 3.0 компилятор позволяет явно не указывать тип переменной, тип определяется по выражению справа от знака равенства:

var i = 0; //неявно типизированная переменная

Функция

Определяется как порция кода, выполняющая определенную задачу.

Рассмотрим возможности функций в C# 1.0:

void foo(int i, ref int j, out int k, params int[] l)
{
  if (l.Length > 0)
    j = l[0];
  k = 10;
}
int j = 5;
int k;
foo(10, ref j, out k, 1, 2, 3);

Ключевые слова ref и out позволяют вернуть значение в аргументе, а с помощью params можно сделать функцию с переменным числом аргументов.

В C# 3.0 появились функции-расширения. Они объявляются в одном классе, а вызываются как методы другого класса:

static class C // класс должен быть статическим
{
  public static string foo(this int i) //статический метод и ключевое слово this
  {
    return "Мое значение: " + i;
  }
}
MessageBox.Show(10.foo()); //у типа int появилась новая функция

В C# 4.0 ввели аргументы по-умолчанию и произвольный порядок аргументов при вызове функции:

void foo(int i, int j, int k = 0) //аргументы по-умолчанию должны быть в конце
{}
foo(i: 10, j: 20); //аргументы идут не по порядку

Класс

Тип данных, характеризуемый своими функциями и переменными.

Самый простой класс в C# 1.0:

class C
{} //нет членов класса

В C# 2.0 появились статические конструкторы:

class C
{
  static C()
  {} //вызовется до первого обращения к классу
}

В C# 2.0 допускается частичное объявление классов:

partial class C
{
  int i;
}
partial class C //продолжение объявления того же самого типа
{
  int j;
}

В C# 3.0 можно создавать анонимные типы:

var c = new { I = 10, J = 20 }; //объект с двумя свойствами
MessageBox.Show(c.J.ToString()); //свойство доступно только для чтения

Делегат

Реализует механизм обратного вызова функций. Делегаты можно передавать в параметрах функций.

Объявим делегат и функцию с параметром-делегатом:

delegate int BinaryFunction(int i, int j);
void ShowBinaryFunctionInfo(BinaryFunction op, int i, int j)
{
  MessageBox.Show(op(i, j).ToString());
}

Вызов данной функции в C# 1.0:

int Sum(int i, int j)
{
  return i + j;
}
BinaryFunction op = new BinaryFunction(Sum);
ShowBinaryFunctionInfo(op, 10, 20);

В C# 2.0 появились анонимные методы, позволяющие сэкономить на объявлении функции:

ShowBinaryFunctionInfo(delegate(int i, int j) { return i + j; }, 10, 20);

В C# 3.0 ввели лямбда-выражения, упрощающие анонимные методы:

ShowBinaryFunctionInfo((i, j) => { return i + j; }, 10, 20);

Свойство

Работа с объектом, как правило, осуществляется через интерфейс. Интерфейс не может содержать переменные, вместо переменных используются свойства.

Объявим интерфейс:

interface I
{
  int Data { get; set; } //2 метода: get_Data и set_Data, они же 1 свойство
}

Реализация интерфейса в C# 1.0:

class C: I
{
  int data = 0;
  public int Data //реализовали свойство
  {
    get
    {
      return data;
    }
    set
    {
      Data = value;
    }
  }
} 
I obj = new C();
obj.Data = 10;
MessageBox.Show(obj.Data.ToString());

Благодаря автоматическим свойствам в C# 3.0 код сильно укорачивается:

class C: I
{
  public int Data {get; set;} //компилятор сам пишет нужный код
}
I obj = new C();
obj.Data = 10;
MessageBox.Show(obj.Data.ToString());

Кроме того, в C# 3.0 свойства можно инициализировать при создании объекта:

I obj1 = new C() { Data = 10 };

Обобщенное программирование

Согласно этой парадигме алгоритмы должны применяться к различным типам объектов.

Самая простая реализация этой парадигмы – использование типа object в C# 1.0:

void Swap(ref object item1, ref object item2)
{
  object tmp = item1;
  item1 = item2;
  item2 = tmp;
}
object i = 10, j = 20;
Swap(ref i, ref j);

В C# 2.0 ввели параметризованные классы и функции:

class C<T> //пример класса
{
  public T data;
}
C<int> c = new C<int>();
c.data = 10;

void Swap<T>(ref T item1, ref T item2) //пример функции
{
  T tmp = item1;
  item1 = item2;
  item2 = tmp;
}
int i = 10, j = 20;
Swap<int>(ref i, ref j);

В C# 4.0 допускается преобразование обобщенных типов:

delegate void Action<in T>(T item); //ключевое слово in
void Display(object obj)
{
  MessageBox.Show(obj.ToString());
}
class C
{ }
Action<C> display = Display; //аргумент функции object, а не C
display(new C());

Позднее связывание

На этапе компиляции может быть неизвестно точное поведение кода. В этом случае контроль корректности вызова методов осуществляется на этапе выполнения программы.

Предварительно объявим вспомогательный класс:

class C
{
  public int foo(int i)
  {
    return i + 1;
  }
}

В C# 1.0 есть библиотека System.Reflection, предоставляющая информацию о типах:

Assembly a = Assembly.GetExecutingAssembly();
object obj = a.CreateInstance("WindowsFormsApplication1.C");
MethodInfo mi = obj.GetType().GetMethod("foo");
int i = (int)mi.Invoke(obj, new object[] { 0 });

В C# 4.0 данный пример можно укоротить:

Assembly a = Assembly.GetExecutingAssembly();
dynamic obj = a.CreateInstance("WindowsFormsApplication1.C"); //динамический тип
int I = obj.foo(0);

Язык запросов

Предоставляет возможность поиска и манипулирования различными источниками данных.

В C# 3.0 появился встроенный язык запросов. Пример запроса для объектов в памяти:

var i1 = from c in new int[] {1,2,3} where c > 2 select c;
MessageBox.Show(i1.First().ToString()); //выведет 3

Пост кода в блог

Нашел очень полезный преобразователь кода C# в html для блога:
http://www.manoli.net/csharpformat/