Причины использования DownCast

1,00
р.
Для чего применяется DownCast в C#? С UpCast всё понятно, но вот с этим вообще ничего не понятно. Зачем его делать?

Ответ
В хорошо спроектированной системе необходимость в понижающем приведении (downcast'е) минимальна. Однако, существуют случаи, при которых он нужен. Вот несколько примеров. Обычная причина — слабость системы типов.
Представьте себе, что вы хотите запрограммировать класс с выделенным экземпляром. Но этот класс можно модифицировать, унаследовавшись от него. Это приводит к примерно такому коду: namespace System.Windows { class Application { static public Application Current { get private set } public Application() { Current = this } } }
namespace MyCoolApp { class App : Application { public MainVM MainVM { get private set } }
class MyControl { // ... var mainVM = ((App)Application.Current).MainVM } }
Знакомый код, не правда ли? Такое приходится писать потому, что система типов не может выразить требование, чтобы Current было актуального типа, чтобы можно было написать App.Current. К аналогичной проблеме приводит попытка описать класс, который должен иметь оператор сравнения с другим экземпляром того же типа. Другой валидный случай использования понижающего приведения — виртуализация по типу объекта вне иерархии классов. Представьте себе, что вам нужно сделать разное поведение кода для разных классов в одном дереве наследования. Обычно это решается при помощи виртуальных функций, но что, если функциональность не имеет отношения к самому объекту? Допустим, у вас есть иерархия типов геометрических фигур, и вы хотите добавить «сбоку» метод печати на консоль. Вносить этот метод абстрактным в базовый класс нет смысла, потому что вывод на консоль не имеет прямого отношения к геометрической фигуре. Поэтому приходится писать сторонний метод, выяснять runtime-тип объекта и приводить к нему. Ещё одно место, где приходится заниматься подобным — паттерн Visitor с крайне уродливой классической имплементацией (без dynamic). Это, наверное, тоже можно считать слабостью системы типов, по крайней мере в будущих версиях языка это должно решаться другими средствами. Ещё один случай — существующий код, к «общему знаменателю» которого приходится приводить свой код. Например, много кода опирается на интерфейс INotifyPropertyChanged, в котором значения свойств представляются наиболее широким типом — object. А значит, для работы с реальным значением нужен downcast. По сути, можно было бы отказаться от нетипизированного INotifyPropertyChanged, и пользоваться типизированным IObservable для каждого свойства отдельно, но для начала придётся уговорить компанию Microsoft переписать WPF! Ну и последний случай, который я вижу — необходимость «залатать» дыру, которую невозможно залатать правильными средствами из-за внешних условий. Например, нужно бы переделать иерархию классов, но дедлайн завтра. Или код производного класса поставляется другим отделом. Здесь может оказаться необходимым нарушить LSP и сделать что-то по-другому для конкретного порождённого класса.