C# — простой язык, благодаря простоте живёт и PHP. Но в то же время он весьма функциональный, и имеет статус «гибридного» языка, совмещая в себе различные парадигмы, встроенную поддержку как императивного стиля программирования, так и функционального.
Как и любой язык, шарп имеет свои тонкости, особенности, «подводные камни» и малоизвестные возможности. Что я имею ввиду? Читайте под катом…
Упаковка и распаковка — знают все, да не каждый
Ссылочные типы (object, dynamic, string, class, interface, delegate) хранятся в управляемой куче, типы значений (struct, enum; bool, byte, char, int, float, double) — в стеке приложения (кроме случая, когда тип значения является полем класса). Преобразование типа значений к ссылочному типу сопровождается неявной операцией упаковки (boxing) — помещение копии типа значений в класс-обёртку, экземпляр которого сохраняется в куче. Упаковочный тип генерируется CLR и реализует интерфейсы сохраняемого типа значения. Преобразование ссылочного типа к типу значений вызывает операцию распаковки (unboxing) — извлечение из упаковки копии типа значения и помещение её в стек.
using System;
class Program
{
static void Main()
{
int val = 5;
object obj = val; // присваивание сопровождается упаковкой
int valUnboxed = (int)obj; // приведение вызовет распаковку
}
}
Соответствующий IL-код:
.locals init ([0] int32 val, [1] object obj, [2] int32 valUnboxed)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: box [mscorlib]System.Int32
IL_0009: stloc.1
IL_000a: ldloc.1
IL_000b: unbox.any [mscorlib]System.Int32
IL_0010: stloc.2
IL_0011: ret
Упаковка и распаковка являются относительно медленными операциями (подразумевают копирование), поэтому по возможности следует их избегать. Нижеследующий код отображает неочевидные случаи, приводящие к упаковке:
using System;
class Program
{
static void Main()
{
// 1. Преобразование типа значений в ссылку на реализуемый им интерфейс
IComparable<int> iComp = 1;
// 2. Преобразование типа enum в ссылку на System.Enum
Enum format = UriFormat.Unescaped;
// 3. Преобразование типа значений к типу dynamic
dynamic d = 1;
}
}
В msdn рекомендуется избегать типов значений в случаях, когда они должны быть упакованы много раз, например в не универсальных классах коллекций (ArrayList). Упаковки-преобразования типов значений можно избежать с помощью универсальных коллекций (System.Collections.Generic namespace). Также следует помнить, что dynamic на уровне IL-кода — это тот же object, только (не всегда) помеченный атрибутами.
Рекурсия в лямбдах — о зловредном замыкании
Обратимся к классической реализации рекурсивного вычисления факториала при помощи лямбда-выражений:
using System;
using System.Numerics;
class Program
{
static void Main()
{
Func<int, BigInteger> fact = null;
fact = x => x > 1 ? x * fact(x - 1) : 1;
Read more: Habrahabr.ru