|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
Почитал в Википедии, почитал тут . Вот что не понял. Рассмотрим ковариацию (прямо по примеру из МСДН) (и сюда ещё приведение типов, наверное, надо). Вот возвращаю я Собаку в случае, когда тип возврата стоит Млекопитающее. Собака содержит больше данных, чем Млекопитающее - она что, при возврате урезается до Млекопитающего? Т. е. при возврате тип Собака приводится к типу Млекопитающего? Теперь контравариация. Я назначаю обработчиком для мыши делегат, который принимает параметром некий обобщённый набор аргументов EventArgs. При этом EventArgs содержит в себе меньше данных, чем MouseEventArgs, т. к. является родителем для MouseEventArgs. Тогда при передаче MouseEventArgs этот объект урезается (приводится к типу) до EventArgs? ... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 11:56 |
|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
авторЯ назначаю обработчиком для мыши делегат Неправильно сказал. Надо "назначаю обработчиком для мыши метод". ... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 11:57 |
|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
user7320, Почему урезается? Просто наследник содержит всю ту информацию, которую содержит его родитель (наследует), поэтому и есть "естественная" возможность передать потомка через родителя. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 12:05 |
|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
Ну тут как. По логике, если я передаю родителя, то я передаю именно родителя, а не его потомка под ним. Когда вводят правило "передаёшь родителя - а это на самом деле потомок", то уже возникает заблуждение. Смотришь в книгу, а видишь фигу. Видишь, что тут родитель (НАПИСАНО ЖЕ "Mammal"!), а получаешь потомка (Dog). Тут логика какая. Вот когда с числами дело имеешь, то приведение типов означает обрезание потомка по родителю. ЭТО ПО ЛОГИКЕ, ЕЩЁ РАЗ! Т. е. по идее, double - это потомок int (double - расширение int). Но попробуйте передать дабл через инт - обрежется до инта. А вот когда объекты передаёшь - всё наоборот. Вот это-то меня и сбивает с толку. Помогите разобраться, пожалуйста. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 12:14 |
|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
user7320Ну тут как. По логике, если я передаю родителя, то я передаю именно родителя, а не его потомка под ним. Когда вводят правило "передаёшь родителя - а это на самом деле потомок", то уже возникает заблуждение. Смотришь в книгу, а видишь фигу. Видишь, что тут родитель (НАПИСАНО ЖЕ "Mammal"!), а получаешь потомка (Dog). Тут логика какая. Вот когда с числами дело имеешь, то приведение типов означает обрезание потомка по родителю. ЭТО ПО ЛОГИКЕ, ЕЩЁ РАЗ! Т. е. по идее, double - это потомок int (double - расширение int). Но попробуйте передать дабл через инт - обрежется до инта. А вот когда объекты передаёшь - всё наоборот. Вот это-то меня и сбивает с толку. Помогите разобраться, пожалуйста. Так твой вопрос сводится к наследованию и инкапсуляции. Одна из ключевых возможностей наследования - Расширение функциональности и сокрытие от клиента того, что он не должен видеть. Т.е. имея клсс Б:А, мы в системе можем использовать только ссылки на А, работать с этим объектом, как с А, но на самом деле используется Б, о котором, клиенту, возможно, ничего не известно. Отсюда то, что ты называешь "обрезанием" :). При необходимости, приведя объект, мы получим новый. С твоим примером. Предположим, что Mammal определяет метод Move, Eat. И программулина, использующая твоих животных, пасет млекопитающих и кормит их; ты можешь передать ей хоть корову, хоть собаку, и прога сможет обращаться с ними одинаково, но поведение этих объектов будет разным. Тут сравнение со значимыми типами (int и double) не корректно. При чем, int & double не наследуют другдругу. И если очень хочется, то можно сделать присвоение родителя потомку, но это извращение редко может где понадобится Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12.
... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 12:47 |
|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
авторТ.е. имея клсс Б:А, мы в системе можем использовать только ссылки на А, работать с этим объектом, как с А, но на самом деле используется Б, о котором, клиенту, возможно, ничего не известно. Отсюда то, что ты называешь "обрезанием" :). При необходимости, приведя объект, мы получим новый. Тогда получается так, что получая на входе массив объектов А, мы должны сначала проверить всех них, являются ли они на самом деле А, или некоторые из них всё же Б, перед тем, как с ними работать? авторПри чем, int & double не наследуют другдругу. Я имел ввиду "по логике". В математике целые являются подмножеством вещественных, а вещественные, следовательно - расширением целых. Вот эта логика и нарушается в С# для чисел, но сохраняется для классов. В C# своя логика. Вобщем, понятно. Логически это вывести нельзя (почему для одних так, а для других - этак) - нужно просто запомнить. Я вот раньше использовал всякие классы и методы из Дотнета, и особо не задумывался, как они работают. Ну знал, там, что многие из них работают не с конкретными типами, а с интерфейсами, и что это удобно иногда. А когда начал задумываться, как это всё устроено и какой в этом смысл, то попал в логическую ловушку, когда попытался сравнить с отношениями объектов и логикой реального мира. Теперь надо, получается, для каждого метода каждого класса запоминать, какое правило (ковариация, контравариация, инвариация) работает для их входных и выходных данных? Что-то сильно сложно выходит... ... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 13:36 |
|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
авторТеперь надо, получается, для каждого метода каждого класса запоминать, какое правило (ковариация, контравариация, инвариация) работает для их входных и выходных данных? Что-то сильно сложно выходит... Это я к тому, что в МСДНе в некоторых статьях по методам и классам прямо указывают правило вариативности для входных и выходных параметров, а в некоторых нет (нужно догадываться?). Ещё про потокобезопасность иногда говорят... Блин, сложно выходит - столько фиг запомнишь, а каждый раз лезть в МСДН, чтобы узнавать каждую маленькую деталь работы каждого метода - это ж с ума сойти можно. Нельзя просто взять и использовать, особо не задумываясь... ... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 13:40 |
|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
авторТогда получается так, что получая на входе массив объектов А, мы должны сначала проверить всех них, являются ли они на самом деле А, или некоторые из них всё же Б, перед тем, как с ними работать? По-вашему, лучше написать десяток-другой перегруженных методов? для работы со всеми потомками базового класса? Если приложение очень сильно завязно на конкретные реализации, часто это может усложнить код: куча перегруженных методов, множество связей между классами. Все это усложняет сопровождение кода, т.к. поменяв реализацию уже используемого конкретного класса нужно отыскивать все места его использования. Да это не критично в небольших проектах, но если есть вероятность его разрастания и подключение новых разработчиков - тут, кмк, нужно максимально использовать абстракцию. Про понимание и запоминание: не все можно сразу понять без практики применения, только на примерах из книг. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 13:53 |
|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
user7320Тогда получается так, что получая на входе массив объектов А, мы должны сначала проверить всех них, являются ли они на самом деле А, или некоторые из них всё же Б, перед тем, как с ними работать?Они все являются А, это даже проверять не нужно, потому что А — базовый, родительский. Некоторые из них могут являются еще и Б, нужно проверять именно это. Но если тебе нужно это проверять, значит у тебя ошибка проектирования. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 13:53 |
|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
А по контравариации поясните, пожалуйста: авторЯ назначаю обработчиком для мыши (или кнопки, неважно) метод, который принимает параметром некий обобщённый набор аргументов EventArgs. При этом EventArgs содержит в себе меньше данных, чем MouseEventArgs, т. к. является родителем для MouseEventArgs. Тогда при передаче MouseEventArgs этот объект урезается (приводится к типу) до EventArgs? Код: c# 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19.
... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 14:55 |
|
Ковариация и контравариация, плюс приведение типов - запутался
|
|||
---|---|---|---|
#18+
Та тоже самое, что и в первом случае. Не важно, что передается, важно, что принимается. Принимается EventArgs, а он — базовый объект. Если ты навесил обработчик на KeyDown, а из EventArgs хочешь получить MouseEventArgs, то получишь ошибку. Если принимается MouseEventArgs, и навешиваешь на тот же KeyDown, то не знаю что будет, не пробовал. Либо ошибка компиляции, либо рантайм ошибка при генерации события. ... |
|||
:
Нравится:
Не нравится:
|
|||
07.06.2013, 18:01 |
|
|
start [/forum/topic.php?fid=20&msg=38290217&tid=1404541]: |
0ms |
get settings: |
10ms |
get forum list: |
14ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
64ms |
get topic data: |
10ms |
get forum data: |
3ms |
get page messages: |
49ms |
get tp. blocked users: |
1ms |
others: | 13ms |
total: | 170ms |
0 / 0 |