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/

Deep Dive C# by Sergey Teplyakov

  • 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 staticIEnumerable<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.
    Какое исключение получим? classFoo { 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 staticT 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 staticIEnumerable<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 staticIEnumerable<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.
    Необязательные аргументы и полиморфизм classBase { 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.
    Примерная реализация // Декларация voidWithEmptyString(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)] classRefArrayLayout { [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.
    Значимые типы (ValueTypes) • В чем главная разница между значимыми и ссылочными типами? 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 publicstruct 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.
  • 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# DeepDive 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/

Editor's Notes

  • #3 Тут приходит в голову очень пошлая фотка)
  • #13 Обязательно сказать, что traceability – очень важная составляющая любого приложения.Поэтому если вы где-либо логируете лишь ex.Message, то писать вам всегда на языке С! (это ведь страшнее, чем гореть в аду в страшных муках).
  • #15 Получим &quot;3&quot;.Сказать, что это важно, поскольку эта же самая проблема, но в несколько завуалированном виде возникает достаточно часто!
  • #16 Применимо в том же WCF-е! Когда Dispose приводит к закрытию клиента в результате чего исходное исключение теряется!
  • #17 У жабы в try-with-resources – самый четкий подход!!!Сказать, что у этой проблемы нет простого решения!
  • #18 Да, тут можно сказать, что в С++\CLI все будет зашибись! Как и в старом С++!
  • #19 Продолжаем тему обработки исключений.
  • #20 И теперь мы «упадем» в первой строке!
  • #21 TODO: Рисунок с контрактами!
  • #28 Это не сверх полезная информация, тем не менее, у нее есть и некоторые любопытные особенности.
  • #29 Но прежде чем переходить к
  • #34 Объекты не равны в обоих случаях, но в первом случае методы GetHashCode возвращают один и тот же результат!
  • #35 Посмотрите заметку по ссылке, чтобы понять, о чем идет речь!
  • #37 // TODO: Сделать пометки, почему ведет себя код именно так! Сказать, что readonly – доступ к ней – это не обращение к переменной, а получение значения! Добавить b.M.Y++, что это тоже не компилится, поскольку b.M - rvalue