Так как Вы попросили подсказки по книгам... Насколько отладка Windows идет, у John Robbins есть несколько выпусков хорошей книги по отладке Windows:
Приложения Отладки для Microsoft.NET и Примечание Microsoft Windows
, что новый выпуск ( Отладка Приложения Microsoft.NET 2.0 ) является.NET только, таким образом, Вы могли бы хотеть более старый (как в первой ссылке), если Вы хотите отладку собственного кода (это покрывает и.NET и собственный компонент).
=============
ОБНОВЛЕНИЕ: Я использовал этот ответ в качестве основы для этой записи в блоге:
Почему параметры ref и out не разрешить изменение типа?
Дополнительные комментарии по этому вопросу см. на странице блога. Спасибо за отличный вопрос.
=============
Предположим, у вас есть классы Animal
, Mammal
, Reptile
, Жираф
, Черепаха
и Тигр
, с очевидными подклассами.
Теперь предположим, что у вас есть метод void M (ref Млекопитающее m)
. M
может читать и писать m
.
Можете ли вы передать переменную типа
Animal
вM
?
Нет. Эта переменная может содержать Turtle
, но M
предполагает, что он содержит только млекопитающих. Черепаха
не является млекопитающим
.
Вывод 1 : ref
параметры не могут быть увеличены. (Животных больше, чем млекопитающих, поэтому переменная становится «больше», потому что может содержать больше вещей.)
Можете ли вы передать переменную типа
Giraffe
вM
?
Нет. M
может записывать в m
, а M
может захотеть записать Tiger
в m
. Теперь вы поместили Tiger
в переменную, которая на самом деле имеет тип Giraffe
.
Вывод 2 : ref
параметры не могут быть созданы » меньше ».
Теперь рассмотрим N (вне Млекопитающее n)
.
Можете ли вы передать переменную типа
Giraffe
вN
?
Нет. N
может писать на n
, а N
может захотеть написать Tiger
.
Вывод 3 : параметры out
не могут быть уменьшены.
Можете ли вы передать переменную типа
Animal
вN
?
Хм.
А почему бы и нет? N
не может читать из n
, он может только писать в него, верно? Вы записываете Tiger
в переменную типа Animal
, и все готово, верно?
Неверно. Правило не такое: « N
может писать только на n
».
Вкратце правила таковы:
1) N
должен записать в n
, прежде чем N
вернется нормально. (Если N
выбрасывается, все ставки отменены.)
2) N
должен записать что-то в n
, прежде чем он прочитает что-то из n
.
Это разрешает следующую последовательность событий:
x
типа Animal
. x
как Параметр out
на N
. N
записывает Tiger
в n
, который является псевдонимом для x
. Turtle
в x
. N
пытается прочитать содержимое n
, и обнаруживает Turtle
в том, что он считает переменной типа Mammal
. Очевидно, мы хотим сделать это незаконным.
Вывод 4 : out
параметры не могут быть увеличены.
Окончательный вывод : Ни ref
, ни out
параметры могут иметь разные типы. Поступить иначе - значит нарушить безопасность проверяемого типа.
Если эти вопросы в базовой теории типов интересуют вас, подумайте о прочтении моей серии статей о том, как ковариация и контравариантность работают в C # 4.0 .
Ни ref
, ни out
параметры не могут изменять свои типы. Поступить иначе - значит нарушить безопасность проверяемого типа.
Если эти вопросы в базовой теории типов интересуют вас, подумайте о прочтении моей серии статей о том, как ковариация и контравариантность работают в C # 4.0 .
Ни ref
, ни out
параметры не могут изменять свои типы. Поступить иначе - значит нарушить безопасность проверяемого типа.
Если эти вопросы в базовой теории типов интересуют вас, подумайте о прочтении моей серии статей о том, как ковариация и контравариантность работают в C # 4.0 .
Потому что в обоих случаях вы должны иметь возможность присвоить значение параметру ref / out.
Если вы попытаетесь передать b в метод Foo2 как ссылку, а в Foo2 вы попытаетесь присвоить значение = new A (), это будет недопустимым.
По той же причине, по которой вы не можете писать:
B b = new A();
Вы боретесь с классической проблемой ООП ковариантности (и контравариантности), см. wikipedia : как бы этот факт ни противоречил интуитивным ожиданиям, математически невозможно разрешить замену производных классов вместо базовых для изменяемых (назначаемых) аргументов (а также контейнеров, элементы которых назначаются по той же причине), при этом соблюдая принцип Лискова . Почему это так, описано в существующих ответах и более подробно исследовано в этих вики-статьях и ссылках на них.
ООП-языки, которые, кажется, делают это, оставаясь при этом традиционно статически безопасными, являются «обманом» (вставка скрытых проверок динамического типа или требование проверки ВСЕХ источников во время компиляции); фундаментальный выбор:
Поскольку предоставление Foo2
a ref B
приведет к искажению объекта, потому что Foo2
знает только как заполнить A
часть B
.
Разве компилятор не говорит вам, что он хотел бы, чтобы вы явно привели объект, чтобы он мог быть уверен, что вы знаете свои намерения?
Foo2(ref (A)b)
Учтите:
class C : A {}
class B : A {}
void Foo2(ref A a) { a = new C(); }
B b = null;
Foo2(ref b);
Это нарушит безопасность типов