Принудительно вызывать реализацию производного класса в универсальной функции в C #? - PullRequest
0 голосов
/ 04 мая 2010

Хорошо, так что в настоящее время я работаю с набором классов, которые я не могу контролировать в некоторых довольно общих функциях, использующих эти объекты. Вместо того чтобы писать буквально десятки функций, которые по сути делают одно и то же для каждого класса, я решил вместо этого использовать универсальную функцию.

Теперь классы, с которыми я имею дело, немного странны, так как производные классы имеют много одинаковых свойств, но базовый класс, из которого они получены, не имеет. Одним из таких примеров свойств является .Parent, который существует в огромном количестве производных классов, но не в базовом классе, и именно это свойство мне нужно использовать.

Для простоты понимания я создал небольшой пример:

class StandardBaseClass {} // These are simulating the SMO objects

class StandardDerivedClass : StandardBaseClass {    
    public object Parent { get; set; }    
}

static class Extensions    
{    
        public static object GetParent(this StandardDerivedClass sdc) {
            return sdc.Parent;
        }

        public static object GetParent(this StandardBaseClass sbc)
        {
            throw new NotImplementedException("StandardBaseClass does not contain a property Parent");
        }

        // This is the Generic function I'm trying to write and need the Parent property.    
        public static void DoSomething<T>(T foo) where T : StandardBaseClass
        {
            object Parent = ((T)foo).GetParent();
        }

}

В приведенном выше примере вызов DoSomething () вызовет исключение NotImplemented в реализации GetParent () базового класса, даже если я приведу приведение к T, который является StandardDerivedClass.

Это противоречит другому поведению приведения, когда понижающая версия заставит использовать реализацию базового класса.

Я вижу это поведение как ошибку. Кто-нибудь еще встречался с этим?

Ответы [ 7 ]

4 голосов
/ 04 мая 2010

Я вижу это поведение как ошибку.

Это поведение правильно. Поскольку ваш метод DoSomething ограничивает T до StandardBaseClass, у вас есть доступ только к определенным методам StandardBaseClass, а не к любым методам или свойствам производного класса. Так как StandardBaseClass не имеет свойства Parent, это недопустимо и должно быть недопустимо по своему замыслу.

Здесь есть два возможных варианта: вы можете использовать отражение, чтобы вытащить свойство Parent, или использовать динамический тип C # 4 и рассматривать его как динамический объект. Однако оба они обходят стандартную проверку типов в компиляторе, поэтому вам потребуется выполнить дополнительную проверку типов во время выполнения, чтобы убедиться, что свойство Parent существует.

1 голос
/ 04 мая 2010

Для всех, кто интересуется кратким ответом на эту ситуацию, Стивен Клири ответит на MSDN здесь:

http://social.msdn.microsoft.com/Forums/en-AU/csharpgeneral/thread/95833bb3-fbe1-4ec9-8b04-3e05165e20f8?prof=required

1 голос
/ 04 мая 2010

Создайте интерфейс, который содержит свойство Parent. Пусть каждый класс, имеющий свойство Parent, реализует это взаимодействие. После этого вы сможете создать универсальный метод, который принимает параметр типа IHaveParent, и он будет действовать правильно.

0 голосов
/ 04 мая 2010

Это немного уродливо, но вы можете сделать это, используя систему регистрации, где вы регистрируете делегатов для различных возможных производных классов, которые предоставляют свойство / метод «общего доступа», а затем используете что-то вроде Dictionary<Type,Func<SomeT>> для хранения делегатов. Если вы знаете все производные типы заранее и вам не нужно загружать плагины или тому подобное, вы также можете использовать классическую некрасивую структуру if / else-if. В любом случае вы создаете свою собственную замену тому, что должно было поддерживаться таблицей виртуальных методов.

0 голосов
/ 04 мая 2010

Это правильно, поскольку компилятор знает только, что он может связываться с вашим типом как StandardBaseClass. Привязка не выполняется во время выполнения (где она может потенциально решить использовать перегрузку StandardDerivedClass.

Если вы знаете , что это StandardDerivedClass, то почему бы просто не разыграть его как таковое?

object Parent = ((StandardDerivedClass)foo).Parent;
0 голосов
/ 04 мая 2010

Ваша идея не сработает, потому что компилятор никогда не может гарантировать, что базовый класс действительно имеет такое свойство. И он не просто выберет «правильный», основываясь на том, есть он или нет.

Единственный способ сделать это - использовать отражение и затем проверить во время выполнения, существует ли запрошенное свойство в проверяемом классе. Вы должны судить себя, является ли это приемлемым способом для вашего проекта (рефлексия медленная и требует максимальных прав).

0 голосов
/ 04 мая 2010

Для меня это расхождение в иерархии классов. Под этим я подразумеваю, что либо базовый класс имеет родительский класс, либо производные классы с родительским классом являются производными от абстрактного дочернего элемента базовой.

Lol, как говорит Джон , интерфейса в отличие от абстрактного класса тоже достаточно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...