Deep Dive  C# by Sergey Teplyakov
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Deep Dive C# by Sergey Teplyakov

  • 3,799 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
3,799
On Slideshare
952
From Embeds
2,847
Number of Embeds
5

Actions

Shares
Downloads
6
Comments
0
Likes
1

Embeds 2,847

http://www.usergroup.od.ua 2,841
http://www.blogger.com 2
http://prlog.ru 2
http://1992046596800250872_8b90fc18dbe2c12a9d261f7cee8e1951f69a51c5.blogspot.com 1
http://webcache.googleusercontent.com 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • Тут приходит в голову очень пошлая фотка)
  • Обязательно сказать, что traceability – очень важная составляющая любого приложения.Поэтому если вы где-либо логируете лишь ex.Message, то писать вам всегда на языке С! (это ведь страшнее, чем гореть в аду в страшных муках).
  • Получим "3".Сказать, что это важно, поскольку эта же самая проблема, но в несколько завуалированном виде возникает достаточно часто!
  • Применимо в том же WCF-е! Когда Dispose приводит к закрытию клиента в результате чего исходное исключение теряется!
  • У жабы в try-with-resources – самый четкий подход!!!Сказать, что у этой проблемы нет простого решения!
  • Да, тут можно сказать, что в С++\CLI все будет зашибись! Как и в старом С++!
  • Продолжаем тему обработки исключений.
  • И теперь мы «упадем» в первой строке!
  • TODO: Рисунок с контрактами!
  • Это не сверх полезная информация, тем не менее, у нее есть и некоторые любопытные особенности.
  • Но прежде чем переходить к
  • Объекты не равны в обоих случаях, но в первом случае методы GetHashCode возвращают один и тот же результат!
  • Посмотрите заметку по ссылке, чтобы понять, о чем идет речь!
  • // TODO: Сделать пометки, почему ведет себя код именно так! Сказать, что readonly – доступ к ней – это не обращение к переменной, а получение значения! Добавить b.M.Y++, что это тоже не компилится, поскольку b.M - rvalue

Transcript

  • 1. Deep Dive Сергей Тепляков, Visual C# MVP .NET Architect at Luxoft SergeyTeplyakov.blogspot.com
  • 2. А насколько глубоко будем нырять?
  • 3. Настолько глубоко? 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!!!!
  • 4. Нет! Нет! Нет! • Подробнее об этом треше - http://rsdn.ru/forum/dotnet/3272728.flat • См. этюды nikov-а на rsdn.ru – http://rsdn.ru/Forum/?fuid=55905 • Кури “The C# Programming Language” by Hejlsberg et al!
  • 5. Что нужно для работы цикла foreach? • IEnumerable? • IEnumerable of T? • Что-то еще? • Нужен метод GetEnumerator, возвращающий объект с методом MoveNext и свойством Current!
  • 6. В F# пошли еще дальше… • Поддержку цикла for можно добавить с помощью методов расширения! type Int32 with // Получаем список квадратов чисел от 1 до текущего значения member x.GetEnumerator() = ({1..x} |> Seq.map(fun x -> x*x)).GetEnumerator() // Выводит 1 4 9 16 25 for n in 5 do printf "%d " n
  • 7. Утиная типизация Если кто-то ходит, как утка, и крякает, как утка, то это и есть может быть утка индюшка с утиным адаптером...
  • 8. «Утиная типизация» в C# • foreach • Требуется GetEnumerator, MoveNext и свойство Current • http://sergeyteplyakov.blogspot.com/2012/08/duck-typing- foreach.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 • Методы расширения завязаны не на конкретный тип атрибутов!
  • 9. Блоки итераторов… 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); // 1 var s = seq.Select(line => line.Length); // 2 Console.WriteLine(s.Max()); // 3 Все ли нормально с кодом? Этот же подход используется и для асинхронных методов! Когда получим исключение? Код до первого yield return вызовется при первом вызове метода MoveNext!
  • 10. Какое исключение получим? 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>(); Какое исключение получим?
  • 11. Почему? • Используется reflection (Activator.CreateInstance). • Все обобщения должны содержать одну реализацию! • Вызов метода через Reflection всегда «оборачивает» исходное исключение в TargetInvocationException • «Все нетривиальные абстракции текут» Джоэл Спольски
  • 12. Повторная генерация исключений • Делаем "правильную" фабрику. Наивная реализация: public static T CreateInstanceNaive<T>() where T : new() { try { return new T(); } catch (TargetInvocationException e) { // Исходный стек вызовов потерян, теперь все // будут думать, что виноваты мы! throw e.InnerException; } }
  • 13. Используем ExceptionDispatchInfo public static T CreateInstance<T>() where T : new() { try { return new T(); } catch(TargetInvocationException e) { ExceptionDispatchInfo di = ExceptionDispatchInfo.Capture(e.InnerException); di.Throw(); // компилятор C# не знает, что эта точка недостижима return default(T); } }
  • 14. Quiz #3. Исключение в блоке finally public static void FinallyThatThrows() { try { throw new Exception("1"); } catch(Exception e) { throw new Exception("2"); } finally { throw new Exception("3"); } } try { FinallyThatThrows(); } catch(Exception e) { Console.WriteLine(e.Message); } Что получим? "1", "2" или "3"?
  • 15. Quiz # 4. Маскирование исключений в блоке using class CustomDisposable : IDisposable { public void Dispose() { throw new InvalidOperationException("Ooops!!"); } } // Какое исключение перехватывать? using (var disposable = new CustomDisposable()) { throw new FileNotFoundException(); }
  • 16. Подходы к потерянным исключениям • C# - побеждает исключение из finally. Исходное теряем • С++ • Исключение в деструкторе – UB ;) • std::unexpected, если деструктор вызван при раскрутке стека • Java • Исключение в блоке try-finally • Тоже самое, что и в C# • try-with-resources • Побеждает исключение из finally, но исходное остается в скрытых исключениях
  • 17. Quiz #6. Создание объекта class Base : IDisposable { public Base() { // Выделяем ресурсы! } public void Dispose() { Console.WriteLine("Base.Dispose"); } } class Derived : Base { public Derived(object data) { if (data == null) throw new ArgumentNullException("data"); // Oops!!! } } Что будет в этом случае?
  • 18. Обработка ошибок… 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); // 1 var s = seq.Select(line => line.Length); // 2 Console.WriteLine(s.Max()); // 3 Все ли нормально с кодом? Этот же подход используется и для асинхронных методов! Когда получим исключение? Код до первого yield return вызовется при первом вызове метода MoveNext!
  • 19. Корректная реализация public static IEnumerable<string> ReadByLine(string path) { if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path"); return ReadByLineImpl(path); } private static IEnumerable<string> ReadByLineImpl(string path) { using (var sr = new StreamReader(path)) { string s; while ((s = sr.ReadLine()) != null) yield return s; } } Наличие блока итераторов принципиально изменяет реализацию метода! var seq = ReadByLine(null); // 1 var s = seq.Select(line => line.Length); // 2 Console.WriteLine(s.Max()); // 3
  • 20. В чем разница? • Исключение при вызове метода – ошибка в вызывающем коде • Исключение во время перебора элементов – ошибка/проблема в вызываемом коде!
  • 21. Необязательные аргументы и полиморфизм class Base { public virtual void Foo(int i = 42) { Console.WriteLine("Base.Foo: i = {0}", i); } } class Derived : Base { public override void Foo(int i = 0) { Console.WriteLine("Derived.Foo: i = {0}", i); } } void Main() { Derived d = new Derived(); Base b = d; b.Foo(); d.Foo(); } Вызываем через Base!Вызываем через Derived!
  • 22. Что будет в этом случае? // 1 void WithDateTime(DateTime dt = DateTime.Now) { } // 2 void WithStringEmpty(string s = String.Empty) { } // 3 void WithEmptyString(string s = "") { } // 4 void WithMethodCall(int id = GetInvalidId()) { } static int GetInvalidId() { return -1; }
  • 23. Примерная реализация // Декларация void WithEmptyString(string s = "") { } // Вызов WithEmptyString(); // Декларация [Optional, DefaultParameterValue(value: "")] static void WithEmptyString(string s) {} // Вызов string defaultValue = GetDefaultValueFromMetadata(); WithEmptyString(defaultValue);
  • 24. Можно ли изменить неизменяемое? (или как можно изменить readonly поля?)
  • 25. Вот пример! • Как поменять C1.X? class C1 { public readonly int X = 42; } • Рефлекшн. Но это скушно! class C2 { public int X; } [StructLayout(LayoutKind.Explicit)] class C1Modifier { [FieldOffset(0)] public C1 C1; [FieldOffset(0)] public C2 C2; } var c1 = new C1(); // c1.X == 42 // Можификация одного поля приводит к модификации другого! var c1Modifier = new C1Modifier {C1 = c1}; c1Modifier.C2.X = -1; // c1.X == -1 Console.WriteLine(c1.X); Создаем класс с таким же «расположением» Создаем «объединение» (union) из двух таких классов
  • 26. Ковариантность массивов • Что такое ковариантность массивов? • Вот пример: object[] o = new string[]{"s1", "s2", "s3"}; • Что в этом опасного? o[0] = new StringBuilder(); • Это требует проверки типа аргумента во время исполнения! • Почему? • Так было в Java!! ArrayTypeMismatchException!
  • 27. Как устроен массив внутри?
  • 28. Внутреннее устройство массива Flags, ... Length (4) 42 (Element 0) 41 (Element 1) 40 (Element 2) 39 (Element 3) arr[0] var arr = new int[] {42, 41, 40, 39}; Flags, ... Length (4) Type Indentifier (System.String) 1 (Element 0) 2 (Element 1) 3 (Element 2) 4 (Element 3) arr[0] var arr = new string[] {"1", "2", "3", "4"};
  • 29. Изменяем размер массива [StructLayout(LayoutKind.Explicit)] class RefArrayLayout { [FieldOffset(0)] public int Length; } [StructLayout(LayoutKind.Explicit)] class ArrayExplorer { [FieldOffset(0)] public object[] Array; [FieldOffset(0)] public RefArrayLayout Layout; } var array = new string[] {"1", "2"}; // array.Length = 2 var ae = new ArrayExplorer {Array = array}; ae.Layout.Length = 42; // Получаем 42 Console.WriteLine(array.Length);
  • 30. Значимые типы (Value Types) • В чем главная разница между значимыми и ссылочными типами? The most relevant fact about value types is not the implementation detail of how they are allocated, but rather the by-design semantic meaning of “value type”, namely that they are always copied “by value” Eric Lippert
  • 31. Метод GetHashCode • Нужно ли переопределять у структур метод GetHashCode? • Какова реализация этого метода по умолчанию? • Корректна ли она?
  • 32. Пример встроенного метода GetHashCode public struct KeyValuePair<TKey, TValue> { public TKey Key; public TValue Value; } public struct KeyValuePair { public static KeyValuePair<TKey, TValue> Create<TKey, TValue>( TKey key, TValue value) { return new KeyValuePair<TKey, TValue>() { Key = key, Value = value }; } } Используем реализацию Equals и GetHashCode по умолчанию
  • 33. Тонкости реализации • KeyValuePair<int, string>: var kvp1 = KeyValuePair.Create(42, "Foo"); var kvp2 = KeyValuePair.Create(42, "Boo"); Console.WriteLine(kvp1.GetHashCode() == kvp2.GetHashCode()); Console.WriteLine(kvp1.Equals(kvp2)); • KeyValuePair<int, int>: var kvp1 = KeyValuePair.Create(42, 42); var kvp2 = KeyValuePair.Create(42, 43); Console.WriteLine(kvp1.GetHashCode() == kvp2.GetHashCode()); Console.WriteLine(kvp1.Equals(kvp2)); • Одинаковый ли хэш-коды? • Равны ли объекты?
  • 34. Тонкости реализации (2) • ValueType.GetHashCode() содержит две реализации: • Быструю и точную: • Структура не содержит «пробелов» в содержимом структуры и ссылочных типов. • Используется бинарное представление всего содержимого структуры. • Быструю и неточную: • Структура содержит «пробелов» в содержимом или ссылочные типы. • Используется лишь первое поле структуры. • Подробнее – Why is ValueType.GetHashCode() implemented like it is?
  • 35. Нееееттт!!!!1111
  • 36. Вопросы?
  • 37. Чего еще почитать? • Programming Stuff • C# Tips and Tricks • Chris Burrows’ Blog • Eric Lippert’s Blog • Joe Duffy’s Weblog • B# .NET BLOG
  • 38. More C# Deep Dive on Programming Stuff • this == null? • Замыкания в языке C# • Перегрузка и наследование • Структуры и конструкторы по умолчанию • О вреде изменяемых значимых типов. • Часть 1 • Часть 2 • MVP Summit. День 0. Кэширование делегатов • MVP Summit. День 1. Об эффективности
  • 39. Спасибо за внимание • Сергей Тепляков, Visual C# MVP • .NET Architect at Luxoft • Sergey.Teplyakov@gmail.com • http://sergeyteplyakov.blogspot.com/