C#, asp net.ajax разработка web-приложений, Javascript CSS
 
Задать вопрос asp.net ajax C#

Рубрики


Статьи


Подписка


Подписаться по RSS

Архив

 Полный архив по категориям

Популярные записи


Мои рекомендации



Вывод типа (Type Inference)

При полноценной работе с коллекциями, частенько встречаются ситуации, когда описывать заранее тип переменной неудобно, хотя он и известен на этапе компиляции. На самом деле, так происходит и при обычной работе, но в случае с коллекциями это проявляется наиболее ярко. Более подробно разберем эти ситуации позже...

Поэтому, для облегчения жизни в подобных случаях, вводится такая функциональность как «вывод типа» (type inference). Суть этого дела довольно проста – если компилятор может вычислить тип из правой части выражения, то декларировать его в левой части не обязательно. Дабы не углубляться в излишний формализм, прибегнем к примеру:

// Вместо того, чтобы писать

//

MyLongFooClassWithTemplate<MyLongTypeParameter> local

= new MyLongFooClassWithTemplate<MyLongTypeParameter>();

 

// Можно объявить переменную так:

//

var localVar = new MyLongFooClassWithTemplate<MyLongTypeParameter>();

 

// А можно даже и так:

//

var i = 1;

var s = "SomeString";

var z = i + i * i;

var c = 'c';

 

Console.WriteLine(i.GetType()); // System.Int32

Console.WriteLine(s.GetType()); // System.String

Console.WriteLine(c.GetType()); // System.Char

Console.WriteLine(z.GetType()); // System.Int32

 

Ключевое слово «var», вместо типа переменной, говорит компилятору о том, что тип выражения надо вычислить из правой части... Обратите внимание - именно компилятору, то есть вычисление типа работает не во время выполнения кода, а на этапе компиляции, язык по прежнему статически типизирован и все ошибки связанные с неправильным использованием типов будут отловлены на этапе компиляции.

С этой точки зрения, слово «var» мне кажется не очень удачным, оно вызывает ассоциации с динамически типизированными языками, здесь больше подошло бы «let» или «def», как в некоторых функциональных языках, ну да не важно.. :)

На самом деле, можно считать, что выведение типов есть и в C# 2.0. Если помните, в случае когда есть обобщенный метод, то тип можно не указывать, он вычислится из параметра, например так:

// Если у нас есть такой метод, занимающийся выводом типа на консоль

//

public static void Print<T>(T variable)

{

    Console.WriteLine(variable.GetType());

}

 

// то использовать его можно так:

// (обратите внимание, явно тип параметра нигде не указывается)

//

Print(i); // System.Int32

Print(s); // System.String

 

Но это уж очень урезанная версия данной конструкции. :)

Как можно заметить в C# 3, эту функциональность несколько расширили, однако использовать ее можно только в локальных переменных. Скажем, с членами класса такой фокус уже не сработает...

Справедливости ради надо сказать, что функциональных языках, из которых данный механизм был позаимствован, он используется давно, с удовольствием и гораздо шире, чем будет предложено в третьей версии шарпа. Однако C# - это первый широко используемый язык общего назначения, где подобный функционал вводится в достаточно большем объеме. И если на первый взгляд фича показалась незначительной, то надеюсь весь потенциал раскроется из дальнейших пояснений. К ней еще не раз придется вернуться и именно по этому я начал рассказ о новых возможностях языка именно отсюда.

Анонимные типы.

Вот и пришло время обсудить одну из ситуаций, когда неудобно декларировать тип переменной при ее обьявлении... При полноценной работе с коллекциями тип результата операции или набора операций над коллекциями может сильно отличаться от типов учавствующих в процессе. Данный факт порождает следующую проблему... В строго типизированных языках мы обязаны сначала описать тип данных, а потом уже использовать переменные этого типа. Таким образом, при классическом подходже у нас есть два пути.

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

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

Посему, для избавления от этой проблемы ввели такое понятие как «Анонимные типы», (anonymous type)

Анонимные типы – это возможность создать новый тип, не декларируя его заранее, а непосредственно при создании переменной, по описанию.

Ну опять-таки, ближе к делу, на примере оно всяко понятнее:

// Вот так выглядит объявление нового типа

//

var anon = new { a = 3, b = 4.81, c = "string data" };

 

// дальше можно спокойно использовать

//

Console.WriteLine(“a = “ + anon.a + “\tb = “ + anon.b + “\tc = ” anon.c);

 

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

//

Console.WriteLine(anon.GetType());

Очевидно, созданный подобным образом тип не имеет имени, потому он собственно и анонимный и различается только по сигнатуре входящих в него полей, что хорошо видно при попытке означенное имя запросить.

Как легко можно заметить, здесь так же не обошлось и без функциональности описанной в предыдущем разделе. Причем вывод типов помог здесь аж два раза. Во-первых при объявлении переменной, без этого нам пришлось бы описывать тип дважды и все удовольствие от анонимности типа нивелировалось бы утомительностью его объявления. А во вторых, при описании полей, их тип  нигде не указан и вычисляется из типа инициализирующего выражения.

К сожалению, у анонимного типа есть один досадный недостаток, серьезно ограничивающий возможности его использования. Его нельзя экспортировать за пределы метода в котором его создали. Связано это с тем что при выходе третьей версии C# рантайм не будет изменен, будет использован все тот же рантайм, что поставлялся с .Net Framework 2.0, а в нем не были предусмотрены анонимные типы. По этой причине анонимный тип нельзя сделать видимым за пределами сборки... Возможно, чтобы лишний раз не искушать, видимость анонимных типов ограничили телом метода, но с очень хорошей вероятностью, при выходе следующей версии рантайма, анонимные типы можно будет использовать везде где необходимо...

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

Таким образом, технология анонимных типов в принципе отработана, но у них есть несколько существенных отличий от кортежей. Во-первых, кортежи доступны только для чтения, и значения в полях кортежа поменять нельзя. А во вторых, кортежи не именованы, то есть добраться до значения поля в кортеже можно только по индексу. Последний пункт не является большим недостатком именно для кортежей, типичный сценарий их использования – это два три выходных параметра функции. Но так как создатели C# держали в голове, что одним из основных сценариев использования анонимных типов будет работа с коллекциями, то они совершенно сознательно сделали их именованными. Ну представьте, у типичного объекта User в базе данных 7-8 полей типа string, и добраться до нужного поля по индексу, причем возможно в нескольких разных местах и до разных полей, становится нетривиальной задачей.

Я уделил этому так много внимания потому, что если бы анонимные типы были не именованными, то уже сейчас, даже при текущей версии рантайма, их можно было бы использовать за пределами сборки (что с успехом и делают функциональные языки для .Net, например F#). Но с учетом ориентированности нового функционала именно на работу с коллекциями, ввести вместо кортежей полноценный тип, пусть и с ограниченной областью видимости, кажется более правильным решением.

Предыдущие записи: Используем LINQ. Часть 1

По материалам http://blogs.gotdotnet.ru/personal/bezzus/

Comments are closed.