Новые возможности .NET 4.0: C# 4.0 (исходники)

Источник: gotdotnet
outcoldman

После выхода Visual Studio 2010 - первым делом нужно разобраться, что же дает нового нам C# 4.0 (на время написания статьи была версия beta 1). Первым делом должен вам порекомендовать примеры C# 4.0, которые можно скачать отсюда (там же есть документ New Features in C# 4.0, которая послужила основой для этой стаьи). Документацию по .Net Framework 4.0 можно также посмотреть в MSDN. Итак, давайте посмотрим, что же нам дает C# версии 4.0.

1. Dynamic Language Runtime

Чтобы понять, что такое Dynamic Language Runtime (далее просто DLR), я должен изначально обратить ваш взор на следующую схему, иллюстрирующую архитектуру DLR:

Именно! Теперь в .net можно еще и скриптовые языки использовать, такие как IronRuby и IronPython. Конечно, вряд ли многие будут ими пользоваться, но это дает дополнительные возможности в реализации ваших приложений. Для того, чтобы иметь возможность использовать данные языки необходимо скачать и установить соответствующие проекты:

  • IronPython. - open-source проект на CodePlex.
  • IronRuby. - open-source проект на RubyForge.

Так же, предоставляется исходники DLR, при помощи которых вы, наверняка, сможете создать свой динамический язык для .NET, если у вас есть в этом необходимость.

Теперь давайте рассмотрим подробнее, что из себя представляет DLR. Итак DLR включает в себя Expression Trees (деревье выражения - если переводить дословно), которые просто являются представлением вызовов методов или бинарных операций в виде дерева, их функциональность можно посмотреть на следующем примере

Expression<Func<int, bool>> exprTree = num => num < 5;
// Decompose the expression tree.
ParameterExpression param = (ParameterExpression)exprTree.Parameters[0];
BinaryExpression operation = (BinaryExpression)exprTree.Body;
ParameterExpression left = (ParameterExpression)operation.Left;
ConstantExpression right = (ConstantExpression)operation.Right;
Console.WriteLine("Decomposed expression: {0} => {1} {2} {3}", param.Name, left.Name, operation.NodeType, right.Value);

В этом примере мы сначала описываем лямбда выражение num=>num<5, а затем при помощи объектов от Expression Trees разбираем данное выражение.

Call Site caching в DLR - это и есть динамическое представление вызовов методов динамических объектов или операций над динамическим объектами. DLR кеширует характеристики объектов (о типах объектах), а так же об операции, и если данная операция уже была выполнена ранее, тогда всю необходимую информацию DLR получит уже из кеша.

И последнее в DLR - это набор классов и интерфейсов: IDynamicMetaObjectProvider, DynamicMetaObject, DynamicObject и ExpandoObject. Давайте опять посмотрим на примере, как нам это может пригодиться, и зачем нам вообще нужен этот DLR:  blockquote { background-color:white; border-left: 2px solid gray; margin-left:5px; padding: 5px 10px 5px 10px; }

class Test1
{
}
static void Main(string[] args)
{
dynamic t = new Test1();
string str = t.Hello(); // Error 1
dynamic d = 7.0;
int i = d; // Error 2
}

На удивление данный код скомпилируется и запустится. Все дело в волшебном слове dynamic, оно нам позволяет вызывать любые по имени свойства или методы, а так же приводить объект к любому типу. Во время Runtime (выполнения кода) вылетят ошибки, Error 1: о том, что метод не найден, Error 2: о том, что double невозможно привести к int. Попробуем их исправить:  для исправления первой ошибки наш класс Test1 отнаследуем от типа System.Dynamic.DynamicObject и перегрузим один из методов, для исправления второй просто явно укажем преобразование типов:

class Test1 : DynamicObject
{
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name == "Hello")
{
result = "Test1 is Dynamic Object!";
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
static void Main(string[] args)
{
dynamic t = new Test1();
string str = t.Hello();
dynamic d = 7.0;
int i = (int) d;
}

Теперь наш код будет работать. Переменная str получить значение "Test1 is dynamic object!", а i значение 7.

Конечно, необязательно наследоваться от класса DynamicObject, можно отнаследоваться и от интерфейса IDynamicMetaObjectProvider, но тогда нужно будет самому реализовывать метод  DynamicMetaObject GetMetaObject(Expression parameter), и более того реализовывать свой тип, унаследованный от DynamicMetaObject, ну в любом случае варианты есть - так что можно взять на вооружение.

2. Именованные и необязательные параметры в методах

Функциональность необязательных параметром существует во многих языках, в C# она же приходит только в версии 4.0, и более того здесь же появляются еще и именованные параметры. Теперь есть возможность устанавливать дефолтные значения у параметров методов, а так же возможность установки значения параметра по имени при вызове метода. Давайте рассмотрим на примере:

class Test1 
{
public void Method(int a = 0, string b = "Hello", bool c = true)
{
Console.WriteLine("{0}, {1}, {2}", a, b, c);
}
}
static void Main(string[] args)
{
Test1 o = new Test1();
// Вызовем по как обычно
o.Method(1, "Hello", true);
// А можно поменять порядок параметров
o.Method(b: "hello", c: true, a: 1);
// Можно вообще ничего не вызывать
// (установлены значения по умолчанию у всех параметров)
o.Method();
// Можно определить только необходимые параметры
o.Method(1, "Hello");
// И не обязательно по порядку
o.Method(c: false);
}

Теперь из-за переименование параметра метода, код может и не скомпилироваться, если кто-то использовал установку значения по имени, так что нужно быть аккуратнее. 

В дополнение хочу сказать, что если все таки будет у класса Test1 метод void Method(int a), тогда при вызове o.Method(1) вызовится именно он, а не метод из примера с дефолтными значениями.

3. Возможности для COM Interop

DLR так же дал новые возможности для COM Interop, теперь  можно COM объекты определять как динамические (точнее они уже являются в большинстве своем динамического типа) и не приводить постоянно получаемые объекты к определенным типам для вызова методов или свойств.

excel.Cells[1, 1].Value = "Hello";
// вместо
((Excel.Range)excel.Cells[1, 1]).Value2 = "Hello";

Данный пример взят из документа New Futures in C# 4.0 С одной стороны приятно, что теперь не нужно мучаться и находить к какому же типу нужно привести объект, чтобы вызвать его свойство или метод, но с другой стороны теряется IntelliSense.

4. Новое в generic

В C# 4.0 обогатился и generic новой функциональностью. Можно теперь у интерфейсов и у делегатов перед определением generic типов писать out и in, зачем это чуть дальше, а сначала рассмотрим пример.При работе с generic часто хочется сделать что то типа такого: 

IList<string> strings = new List<string>();
IList<object> objects = strings;

Но нельзя. Потому, что следом можно написать: 

objects[0] = 5;
string s = strings[0];

То есть, изначально у нас был список строк, потом обозначили его как список объектов, и хотим уже работать с ним, как с объектами, установливая любой другой объект в него, хотя список до сих пор является списком строк. Но, если вдуматься, то можно представит, что если бы список был только для чтения, то мы бы уже не смогли ничего нарушить, и там бы логика была ясна, потому следующий код на C# 4.0 будет работать:

IEnumerable<object> objects = strings;

Огромную полезность данная функциональность принесет в работе с linq, там часто возникают проблемы, что возвращаем объекты одного типа, а нужно получить список другого типа (базового).Итак, как же такое стало возможным. Сначала рассмотрим слово out. Теперь интерфейс IEnumerable<T> объявлен как IEnumerable<out T>, где out обозначает, что тип T может быть использован только для возвращения значений, в другом случае компилятор будет ругаться, ну и более того это дает нам, что интерфейс IEnumerable<A> так же есть и IEnumerable<B>, если у A есть возможность приведения типа к B, если на простом примере, то IEnumerable<string>, есть теперь и IEnumerable<object>. Вот пример:

public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IEnumerator
{
bool MoveNext();
T Current { get; }
}

Есть еще слово in. Его так же можно использовать в описании generic делегатов и интерфейсов. Несет оно такую же смысл как и слово out, только в данному случае описанный тип можно использовать только в передаче параметров, вот пример:

public interface IComparer<in T>
{
public int Compare(T left, T right);
}

То есть в данном случае, если IComparer<object> может считаться и IComparer<string>, потому как если уж он может сравнивать объекты типа object, то и string тоже может.

Так же, как я уже сказал, слова out и in можно применять и к интерфейсам, так, например: 

public delegate TResult Func<in TArg, out TResult>(TArg arg);

 

Заключение

Также в .NET 4.0 появилось много новвовведений, таких как  Lazy Initialiation - память под объект выделяется тогда, когда это действительно становиться нужно. Появились новые типы, как например, BigInteger - теперь не нужно для лабораторных работ студентам писать свои классы для работы с большими числами, SortedSet<T> - класс представляет собой самостоятельное балансированное дерево, которое сохраняет данные в отсортированном порядке после вставки, удаления и поиска. В общем есть еще что изучать.


Страница сайта http://185.71.96.61
Оригинал находится по адресу http://185.71.96.61/home.asp?artId=22020