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; }
s...
Нет! Нет! Нет!
• Подробнее об этом треше -
http://rsdn.ru/forum/dotnet/3272728.flat
• См. этюды nikov-а на rsdn.ru –
http:...
Что нужно для работы цикла
foreach?
• IEnumerable?
• IEnumerable of T?
• Что-то еще?
• Нужен метод GetEnumerator, возвраща...
В F# пошли еще дальше…
• Поддержку цикла for можно добавить с помощью методов
расширения!
type Int32 with
// Получаем спис...
Утиная типизация
Если кто-то ходит, как утка, и крякает, как
утка, то это и есть может быть утка индюшка
с утиным адаптеро...
«Утиная типизация» в C#
• foreach
• Требуется GetEnumerator, MoveNext и свойство Current
• http://sergeyteplyakov.blogspot...
Блоки итераторов…
public static IEnumerable<string> ReadByLine(string path)
{
if (string.IsNullOrEmpty(path))
throw new Ar...
Какое исключение получим?
class Foo
{
public Foo() { throw new Exception("Ooops!!"); }
}
static T Create<T>() where T : ne...
Почему?
• Используется reflection (Activator.CreateInstance).
• Все обобщения должны содержать одну реализацию!
• Вызов ме...
Повторная генерация
исключений
• Делаем "правильную" фабрику. Наивная реализация:
public static T CreateInstanceNaive<T>()...
Используем ExceptionDispatchInfo
public static T CreateInstance<T>() where T : new()
{
try
{
return new T();
}
catch(Targe...
Quiz #3. Исключение в блоке
finally
public static void FinallyThatThrows()
{
try {
throw new Exception("1");
}
catch(Excep...
Quiz # 4. Маскирование
исключений в блоке using
class CustomDisposable : IDisposable
{
public void Dispose()
{
throw new I...
Подходы к потерянным
исключениям
• C# - побеждает исключение из finally. Исходное теряем
• С++
• Исключение в деструкторе ...
Quiz #6. Создание объекта
class Base : IDisposable
{
public Base()
{
// Выделяем ресурсы!
}
public void Dispose()
{
Consol...
Обработка ошибок…
public static IEnumerable<string> ReadByLine(string path)
{
if (string.IsNullOrEmpty(path))
throw new Ar...
Корректная реализация
public static IEnumerable<string> ReadByLine(string path)
{
if (string.IsNullOrEmpty(path))
throw ne...
В чем разница?
• Исключение при вызове метода – ошибка в вызывающем
коде
• Исключение во время перебора элементов –
ошибка...
Необязательные аргументы
и полиморфизм
class Base {
public virtual void Foo(int i = 42) {
Console.WriteLine("Base.Foo: i =...
Что будет в этом случае?
// 1
void WithDateTime(DateTime dt = DateTime.Now) { }
// 2
void WithStringEmpty(string s = Strin...
Примерная реализация
// Декларация
void WithEmptyString(string s = "") { }
// Вызов
WithEmptyString();
// Декларация
[Opti...
Можно ли изменить
неизменяемое?
(или как можно изменить
readonly поля?)
Вот пример!
• Как поменять C1.X?
class C1 { public readonly int X = 42; }
• Рефлекшн. Но это скушно!
class C2 { public int...
Ковариантность массивов
• Что такое ковариантность массивов?
• Вот пример:
object[] o = new string[]{"s1", "s2", "s3"};
• ...
Как устроен массив внутри?
Внутреннее устройство массива
Flags, ...
Length (4)
42 (Element 0)
41 (Element 1)
40 (Element 2)
39 (Element 3)
arr[0]
var...
Изменяем размер массива
[StructLayout(LayoutKind.Explicit)]
class RefArrayLayout
{
[FieldOffset(0)]
public int Length;
}
[...
Значимые типы (Value Types)
• В чем главная разница между значимыми и ссылочными
типами?
The most relevant fact about valu...
Метод GetHashCode
• Нужно ли переопределять у структур метод GetHashCode?
• Какова реализация этого метода по умолчанию?
•...
Пример встроенного метода
GetHashCode
public struct KeyValuePair<TKey, TValue>
{
public TKey Key;
public TValue Value;
}
p...
Тонкости реализации
• KeyValuePair<int, string>:
var kvp1 = KeyValuePair.Create(42, "Foo");
var kvp2 = KeyValuePair.Create...
Тонкости реализации (2)
• ValueType.GetHashCode() содержит две реализации:
• Быструю и точную:
• Структура не содержит «пр...
Нееееттт!!!!1111
Вопросы?
Чего еще почитать?
• Programming Stuff
• C# Tips and Tricks
• Chris Burrows’ Blog
• Eric Lippert’s Blog
• Joe Duffy’s Webl...
More C# Deep Dive on
Programming Stuff
• this == null?
• Замыкания в языке C#
• Перегрузка и наследование
• Структуры и ко...
Спасибо за внимание
• Сергей Тепляков, Visual C# MVP
• .NET Architect at Luxoft
• Sergey.Teplyakov@gmail.com
• http://serg...
Upcoming SlideShare
Loading in...5
×

Deep Dive C# by Sergey Teplyakov

3,896

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
3,896
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
9
Comments
0
Likes
1
Embeds 0
No embeds

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

    1. 1. Deep Dive Сергей Тепляков, Visual C# MVP .NET Architect at Luxoft SergeyTeplyakov.blogspot.com
    2. 2. А насколько глубоко будем нырять?
    3. 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. 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. 5. Что нужно для работы цикла foreach? • IEnumerable? • IEnumerable of T? • Что-то еще? • Нужен метод GetEnumerator, возвращающий объект с методом MoveNext и свойством Current!
    6. 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. 7. Утиная типизация Если кто-то ходит, как утка, и крякает, как утка, то это и есть может быть утка индюшка с утиным адаптером...
    8. 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. 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. 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. 11. Почему? • Используется reflection (Activator.CreateInstance). • Все обобщения должны содержать одну реализацию! • Вызов метода через Reflection всегда «оборачивает» исходное исключение в TargetInvocationException • «Все нетривиальные абстракции текут» Джоэл Спольски
    12. 12. Повторная генерация исключений • Делаем "правильную" фабрику. Наивная реализация: public static T CreateInstanceNaive<T>() where T : new() { try { return new T(); } catch (TargetInvocationException e) { // Исходный стек вызовов потерян, теперь все // будут думать, что виноваты мы! throw e.InnerException; } }
    13. 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. 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. 15. Quiz # 4. Маскирование исключений в блоке using class CustomDisposable : IDisposable { public void Dispose() { throw new InvalidOperationException("Ooops!!"); } } // Какое исключение перехватывать? using (var disposable = new CustomDisposable()) { throw new FileNotFoundException(); }
    16. 16. Подходы к потерянным исключениям • C# - побеждает исключение из finally. Исходное теряем • С++ • Исключение в деструкторе – UB ;) • std::unexpected, если деструктор вызван при раскрутке стека • Java • Исключение в блоке try-finally • Тоже самое, что и в C# • try-with-resources • Побеждает исключение из finally, но исходное остается в скрытых исключениях
    17. 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. 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. 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. 20. В чем разница? • Исключение при вызове метода – ошибка в вызывающем коде • Исключение во время перебора элементов – ошибка/проблема в вызываемом коде!
    21. 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. 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. 23. Примерная реализация // Декларация void WithEmptyString(string s = "") { } // Вызов WithEmptyString(); // Декларация [Optional, DefaultParameterValue(value: "")] static void WithEmptyString(string s) {} // Вызов string defaultValue = GetDefaultValueFromMetadata(); WithEmptyString(defaultValue);
    24. 24. Можно ли изменить неизменяемое? (или как можно изменить readonly поля?)
    25. 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. 26. Ковариантность массивов • Что такое ковариантность массивов? • Вот пример: object[] o = new string[]{"s1", "s2", "s3"}; • Что в этом опасного? o[0] = new StringBuilder(); • Это требует проверки типа аргумента во время исполнения! • Почему? • Так было в Java!! ArrayTypeMismatchException!
    27. 27. Как устроен массив внутри?
    28. 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. 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. 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. 31. Метод GetHashCode • Нужно ли переопределять у структур метод GetHashCode? • Какова реализация этого метода по умолчанию? • Корректна ли она?
    32. 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. 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. 34. Тонкости реализации (2) • ValueType.GetHashCode() содержит две реализации: • Быструю и точную: • Структура не содержит «пробелов» в содержимом структуры и ссылочных типов. • Используется бинарное представление всего содержимого структуры. • Быструю и неточную: • Структура содержит «пробелов» в содержимом или ссылочные типы. • Используется лишь первое поле структуры. • Подробнее – Why is ValueType.GetHashCode() implemented like it is?
    35. 35. Нееееттт!!!!1111
    36. 36. Вопросы?
    37. 37. Чего еще почитать? • Programming Stuff • C# Tips and Tricks • Chris Burrows’ Blog • Eric Lippert’s Blog • Joe Duffy’s Weblog • B# .NET BLOG
    38. 38. More C# Deep Dive on Programming Stuff • this == null? • Замыкания в языке C# • Перегрузка и наследование • Структуры и конструкторы по умолчанию • О вреде изменяемых значимых типов. • Часть 1 • Часть 2 • MVP Summit. День 0. Кэширование делегатов • MVP Summit. День 1. Об эффективности
    39. 39. Спасибо за внимание • Сергей Тепляков, Visual C# MVP • .NET Architect at Luxoft • Sergey.Teplyakov@gmail.com • http://sergeyteplyakov.blogspot.com/
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×