• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Deep Dive  C# by Sergey Teplyakov
 

Deep Dive C# by Sergey Teplyakov

on

  • 3,366 views

 

Statistics

Views

Total Views
3,366
Views on SlideShare
700
Embed Views
2,666

Actions

Likes
1
Downloads
5
Comments
0

5 Embeds 2,666

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

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • Тут приходит в голову очень пошлая фотка)
  • Обязательно сказать, что traceability – очень важная составляющая любого приложения.Поэтому если вы где-либо логируете лишь ex.Message, то писать вам всегда на языке С! (это ведь страшнее, чем гореть в аду в страшных муках).
  • Получим "3".Сказать, что это важно, поскольку эта же самая проблема, но в несколько завуалированном виде возникает достаточно часто!
  • Применимо в том же WCF-е! Когда Dispose приводит к закрытию клиента в результате чего исходное исключение теряется!
  • У жабы в try-with-resources – самый четкий подход!!!Сказать, что у этой проблемы нет простого решения!
  • Да, тут можно сказать, что в С++\CLI все будет зашибись! Как и в старом С++!
  • Продолжаем тему обработки исключений.
  • И теперь мы «упадем» в первой строке!
  • TODO: Рисунок с контрактами!
  • Это не сверх полезная информация, тем не менее, у нее есть и некоторые любопытные особенности.
  • Но прежде чем переходить к
  • Объекты не равны в обоих случаях, но в первом случае методы GetHashCode возвращают один и тот же результат!
  • Посмотрите заметку по ссылке, чтобы понять, о чем идет речь!
  • // TODO: Сделать пометки, почему ведет себя код именно так! Сказать, что readonly – доступ к ней – это не обращение к переменной, а получение значения! Добавить b.M.Y++, что это тоже не компилится, поскольку b.M - rvalue

Deep Dive  C# by Sergey Teplyakov Deep Dive C# by Sergey Teplyakov Presentation Transcript

  • Deep Dive Сергей Тепляков, Visual C# MVP .NET Architect at Luxoft SergeyTeplyakov.blogspot.com
  • А насколько глубоко будем нырять?
  • Настолько глубоко? 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!!!!
  • Нет! Нет! Нет! • Подробнее об этом треше - http://rsdn.ru/forum/dotnet/3272728.flat • См. этюды nikov-а на rsdn.ru – http://rsdn.ru/Forum/?fuid=55905 • Кури “The C# Programming Language” by Hejlsberg et al!
  • Что нужно для работы цикла foreach? • IEnumerable? • IEnumerable of T? • Что-то еще? • Нужен метод GetEnumerator, возвращающий объект с методом MoveNext и свойством Current!
  • В 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
  • Утиная типизация Если кто-то ходит, как утка, и крякает, как утка, то это и есть может быть утка индюшка с утиным адаптером...
  • «Утиная типизация» в 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 • Методы расширения завязаны не на конкретный тип атрибутов!
  • Блоки итераторов… 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!
  • Какое исключение получим? 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>(); Какое исключение получим?
  • Почему? • Используется reflection (Activator.CreateInstance). • Все обобщения должны содержать одну реализацию! • Вызов метода через Reflection всегда «оборачивает» исходное исключение в TargetInvocationException • «Все нетривиальные абстракции текут» Джоэл Спольски
  • Повторная генерация исключений • Делаем "правильную" фабрику. Наивная реализация: public static T CreateInstanceNaive<T>() where T : new() { try { return new T(); } catch (TargetInvocationException e) { // Исходный стек вызовов потерян, теперь все // будут думать, что виноваты мы! throw e.InnerException; } }
  • Используем 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); } }
  • 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"?
  • Quiz # 4. Маскирование исключений в блоке using class CustomDisposable : IDisposable { public void Dispose() { throw new InvalidOperationException("Ooops!!"); } } // Какое исключение перехватывать? using (var disposable = new CustomDisposable()) { throw new FileNotFoundException(); }
  • Подходы к потерянным исключениям • C# - побеждает исключение из finally. Исходное теряем • С++ • Исключение в деструкторе – UB ;) • std::unexpected, если деструктор вызван при раскрутке стека • Java • Исключение в блоке try-finally • Тоже самое, что и в C# • try-with-resources • Побеждает исключение из finally, но исходное остается в скрытых исключениях
  • 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!!! } } Что будет в этом случае?
  • Обработка ошибок… 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!
  • Корректная реализация 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
  • В чем разница? • Исключение при вызове метода – ошибка в вызывающем коде • Исключение во время перебора элементов – ошибка/проблема в вызываемом коде!
  • Необязательные аргументы и полиморфизм 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!
  • Что будет в этом случае? // 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; }
  • Примерная реализация // Декларация void WithEmptyString(string s = "") { } // Вызов WithEmptyString(); // Декларация [Optional, DefaultParameterValue(value: "")] static void WithEmptyString(string s) {} // Вызов string defaultValue = GetDefaultValueFromMetadata(); WithEmptyString(defaultValue);
  • Можно ли изменить неизменяемое? (или как можно изменить readonly поля?)
  • Вот пример! • Как поменять 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) из двух таких классов
  • Ковариантность массивов • Что такое ковариантность массивов? • Вот пример: object[] o = new string[]{"s1", "s2", "s3"}; • Что в этом опасного? o[0] = new StringBuilder(); • Это требует проверки типа аргумента во время исполнения! • Почему? • Так было в Java!! ArrayTypeMismatchException!
  • Как устроен массив внутри?
  • Внутреннее устройство массива 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"};
  • Изменяем размер массива [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);
  • Значимые типы (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
  • Метод GetHashCode • Нужно ли переопределять у структур метод GetHashCode? • Какова реализация этого метода по умолчанию? • Корректна ли она?
  • Пример встроенного метода 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 по умолчанию
  • Тонкости реализации • 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)); • Одинаковый ли хэш-коды? • Равны ли объекты?
  • Тонкости реализации (2) • ValueType.GetHashCode() содержит две реализации: • Быструю и точную: • Структура не содержит «пробелов» в содержимом структуры и ссылочных типов. • Используется бинарное представление всего содержимого структуры. • Быструю и неточную: • Структура содержит «пробелов» в содержимом или ссылочные типы. • Используется лишь первое поле структуры. • Подробнее – Why is ValueType.GetHashCode() implemented like it is?
  • Нееееттт!!!!1111
  • Вопросы?
  • Чего еще почитать? • Programming Stuff • C# Tips and Tricks • Chris Burrows’ Blog • Eric Lippert’s Blog • Joe Duffy’s Weblog • B# .NET BLOG
  • More C# Deep Dive on Programming Stuff • this == null? • Замыкания в языке C# • Перегрузка и наследование • Структуры и конструкторы по умолчанию • О вреде изменяемых значимых типов. • Часть 1 • Часть 2 • MVP Summit. День 0. Кэширование делегатов • MVP Summit. День 1. Об эффективности
  • Спасибо за внимание • Сергей Тепляков, Visual C# MVP • .NET Architect at Luxoft • Sergey.Teplyakov@gmail.com • http://sergeyteplyakov.blogspot.com/