C# Deep Dive

29
Deep Dive Сергей Тепляков, Visual C# MVP .NET Architect at Luxoft SergeyTeplyakov.blogspot.com

description

31 мая – 1 июня в Киеве состоялась конференция HOTCODE 2013. Сергей Тепляков, эксперт Luxoft Training по .Net, С++ и архитектуре приложений, выступил с докладом «C# Deep Dive». Тезисы доклада: «Когда-то в далеком 2002-м году язык C# был прост, как 2 копейки. Но у любого «живого» языка есть одна особенность, приятная и неприятная одновременно — в язык начинают добавляться новые возможности, чтобы наши с вами типовые задачи решались проще и эффективнее. Но с каждой новой возможностью появляются и свои тонкости, незнание которых может лишить столь нужных в нашей жизни конечностей, причем иногда самым изощренным образом. А поскольку язык C# развивается очень динамично, то за время жизни на его просторах появилось много маленьких грабелек, которые мы с вами и научимся обходить ;)».

Transcript of C# Deep Dive

Page 1: C# Deep Dive

Deep DiveСергей Тепляков, Visual C# MVP

.NET Architect at LuxoftSergeyTeplyakov.blogspot.com

Page 2: C# Deep Dive

Анасколько глубоко ?будемнырять

Page 3: C# Deep Dive

?Настолько глубокоclass X { public const int Value = 1000; }

static int Foo(Func<int?, byte> x, object y) { return 1; }static int Foo(Func<X, byte> x, string y) { return 2; }

var a = Foo(X => (byte)X.Value, null);unchecked{ Console.WriteLine(a);}

unchecked{ var a = Foo(X => (byte)X.Value, null); Console.WriteLine(a);}

unchecked{ var a = Foo(X => (byte)X.Value, (object)null); Console.WriteLine(a);} Увидим 1Увидим 2Снова 1!!!!

Page 4: C# Deep Dive

! ! !Нет Нет Нет• Подробнее об этом треше -

http://rsdn.ru/forum/dotnet/3272728.flat• См. этюды nikov-а на rsdn.ru –

http://rsdn.ru/Forum/?fuid=55905• Кури “The C# Programming Language” by Hejlsberg et al!

Page 5: C# Deep Dive

Чтонужнодляработы цикла foreach?

• IEnumerable?• IEnumerable of T?• Что-то еще?

• Нужен метод GetEnumerator, возвращающий объект с методом MoveNext и свойством Current!

Page 6: C# Deep Dive

В F# …пошлиещедальше• Поддержку цикла for можно добавить с помощью методов

расширения!

type Int32 with    // Получаем список квадратов чисел от 1 до текущего значения    member x.GetEnumerator() =        ({1..x} |> Seq.map(fun x -> x*x)).GetEnumerator()

// Выводит 1 4 9 16 25for n in 5 do printf "%d " n

Page 7: C# Deep Dive

Утинаятипизация

Если кто-то ходит, как утка, и крякает, как утка, то это и есть может быть утка индюшка с утиным адаптером...

Page 8: C# Deep Dive

« » Утинаятипизация в C#• foreach

• Требуется GetEnumerator, MoveNext и свойство Current• http://sergeyteplyakov.blogspot.com/2012/08/duck-typing-forea

ch.html• LINQ (Query Comprehension syntax)

• Требуются методы Select, Where, GroupBy etc.• Collection initializer

• Требуется метод Add• C# 5.0 Async Features

• Требуются GetAwaiter() и методы BeginAwait(Action) и EndAwait(), GetResult() и свойства IsCompleted.

• System.Runtime.CompilerServices.ExtensionAttribute• Методы расширения завязаны не на конкретный тип

атрибутов!

Page 9: C# Deep Dive

…Блокиитераторовpublic static IEnumerable<string> ReadByLine(string path){    if (string.IsNullOrEmpty(path))        throw new ArgumentNullException("path");

    using (var sr = new StreamReader(path))    {        string s;        while ((s = sr.ReadLine()) != null)            yield return s;    }}

var seq = ReadByLine(null);              // 1var s = seq.Select(line => line.Length); // 2Console.WriteLine(s.Max());              // 3

Все ли нормально с кодом?

Этот же подход используется и для

асинхронных методов!

Когда получим исключение?

Код до первого yield return вызовется при

первом вызове метода MoveNext!

Page 10: C# Deep Dive

Какоеисключение?получим

class Foo{ public Foo() { throw new Exception("Ooops!!"); }}

static T Create<T>() where T : new(){ var instance = new T(); // Write to log some message return instance;}

var f = Create<Foo>();

Какое исключение получим?

Page 11: C# Deep Dive

?Почему• Используется reflection (Activator.CreateInstance). • Все обобщения должны содержать одну реализацию!• Вызов метода через Reflection всегда «оборачивает»

исходное исключение в TargetInvocationException

• «Все нетривиальные абстракции текут» Джоэл Спольски

Page 12: C# Deep Dive

?Естьлипроблема

using (var file = new FileStream("D:\\1.txt", FileMode.CreateNew)        {            Position = RestoreLastPosition()        }){}

Page 13: C# Deep Dive

Какустроен Object Initializer?class Person{    public string Name { get; set; }    public int Age { get; set; }}

// ...var person = new Person {Name = "Jonh", Age = 42};

var tmp = new Person();tmp.Name = "Jonh";tmp.Age = 42;var person = tmp;

Page 14: C# Deep Dive

Какустроен using?using (var file = new FileStream("D:\\1.txt", FileMode.CreateNew)){}

var file = new FileStream("d:\\1.txt", FileMode.CreateNew);try{}finally{    if (file != null)        ((IDisposable)file).Dispose();}

Page 15: C# Deep Dive

2 2…Складываем иvar tmpFile = new FileStream("d:\\1.txt", FileMode.CreateNew);// Упс! Если мы здесь упадем, то Dispose вызван не будет!tmpFile.Position = RestoreLastPosition();var file = tmpFile;

try{ }finally{    if (file != null)        ((IDisposable)file).Dispose();}

Page 16: C# Deep Dive

Потокобезопасная ?подписканасобытие

class A{    public event EventHandler E;    public void Subscribe(EventHandler e)    {        E = E + e;    }}

var a = new A();EventHandler handler = (o, e) => Console.WriteLine("Handler");a.E += handler; // вызываем из потока 1a.Subscribe(handler); // вызываем из потока 2

Подробнее об этом – Chris Burrow "Events get a little overhaul in C# 4, Part II"

Является ли такая подписка «изнутри» потокобезопасной?

Такой вариант не безопасен!

Page 17: C# Deep Dive

?Какустроенысобытияclass AImpl{    private EventHandler __E;    public event EventHandler E    {        add        {            lock (this) { __E += value; }        }        remove        {            lock (this) { __E += value; }        }    }

    public void Subscribe(EventHandler e)    {        __E = (EventHandler)Delegate.Combine(__E, e);    }

}

В C# 4.0 используется lock-free подход.

А вместо «сырого» Combine для E+= value

вызывается экземплярный Add!

Page 18: C# Deep Dive

В C# 4.0…• События C# 4.0 полностью потокобезопасны• Подписка на событие «изнутри» класса приводит к вызову

Add текущего объекта

// В C# 4.0 решение полностью потокобезопасно!class A{    public event EventHandler E;    public void Subscribe(EventHandler e)    {        E += e;    }    public void RaiseE()    {        var handler = E;        if (handler != null)            handler(this, EventArgs.Empty);    }}

Page 19: C# Deep Dive

…Делегаты

static void SubscribeTo(EventHandler e){    e += (s, ea) => Console.WriteLine("Custom handler");}

EventHandler handler = null;SubscribeTo(handler);handler(null, EventArgs.Empty);

Что произойдет при вызове?

Page 20: C# Deep Dive

– Делегаты…неизменяемы

• Ведь этот трюк все же знают!

class A{    public event EventHandler E;

    public void RaiseE()    {        var handler = E;        if (handler != null)            handler(this, EventArgs.Empty);    }}

Не на 100% thread-safe в теории, но thread-safe на

практике!

Page 21: C# Deep Dive

Виртуальныесобытияclass Base{    public virtual event EventHandler SomeEvent;

    public void RaiseSomeEvent()    {        var handler = SomeEvent;        if (handler != null)            handler(this, EventArgs.Empty);    }}

class Derived : Base{    public override event EventHandler SomeEvent;}

Base b = new Derived();b.SomeEvent += (s, e) =>  Console.WriteLine("Handler");b.RaiseSomeEvent();

Page 22: C# Deep Dive

struct S1{    private readonly int X;    public int GetX() { return X; }}

struct S2{    public int X;}

[StructLayout(LayoutKind.Explicit)]struct S3{    [FieldOffset(0)]    public S1 S1;    [FieldOffset(0)]    public S2 S2;}

var s3 = new S3();s3.S2.X = 42;Console.WriteLine(s3.S1.GetX());

Readonly. ?Правда

X изменить нельзя!Правда ведь?

Да это же union из С/С++!!!

Получим 42!!

Page 23: C# Deep Dive

class ArrayHacker{    public int Length;}

[StructLayout(LayoutKind.Explicit)]class ArrayHack{    [FieldOffset(0)]    public ArrayHacker Hacker;    [FieldOffset(0)]    public int[] Array = new int[2];}

void Main(){    var hack = new ArrayHack();    Console.WriteLine(hack.Array.Length); // 2    hack.Hacker.Length = 42;    Console.WriteLine(hack.Array.Length); // 42!!    hack.Array.Dump(); // Получаем "мусор"}

Изменяемразмер!массива

Первые 4 байта массива – это его размер!

Page 24: C# Deep Dive

Астоитлиихвообще?использовать

• А как же! Ногу, ведь, чем-то нужно отпиливать!• Есть приложения, а есть библиотеки – это разные миры.• Понимание внутреннего устройства сведет проблемы к

минимуму!

Page 25: C# Deep Dive

!!!!1111Нееееттт

Page 26: C# Deep Dive

?Вопросы

Page 28: C# Deep Dive

More C# Deep Dive on Programming Stuff

• this == null?• Замыкания в языке C#• Перегрузка и наследование• Структуры и конструкторы по умолчанию• О вреде изменяемых значимых типов.

• Часть 1• Часть 2

• MVP Summit. День 0. Кэширование делегатов• MVP Summit. День 1. Об эффективности

Page 29: C# Deep Dive

Спасибо за внимание

• Сергей Тепляков, Visual C# MVP• .NET Architect at Luxoft• [email protected]• http://sergeyteplyakov.blogspot.com/