2010-02-09 3 views
5

Хорошо, что я пытаюсь сделать следующее:Способ перегрузки: Воронка вызов перегрузки аргумента производного класса

 protected bool ValidAdvert(Base item) 
     { 
      throw ThisIsAnAbstractClassException(); 
     } 

     protected bool ValidAdvert(Derived1 item) 
     { 
      return ADerived1SpecificPredicate; 
     } 

     protected bool ValidAdvert(Derived2 item) 
     { 
      return ADerived2SpecificPredicate; 
     }

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

Прежде чем кто-то скажет что-то о перегрузке метода на самих классах, логика внутри методов опирается на большое количество различных условий, ни одна из которых не связана, и ни одна из них не связана непосредственно с базовым/производным классом (например, состояние входа и т.д.)

ответ

6

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

protected bool ValidAdvert(Base item) 
{ 
    if (item.GetType() == typeof(Base)) 
     throw new ThisIsAnAbstractClassException(); 

    Type type = typeof(CurrentClass); 

    MethodInfo method = type.GetMethod("ValidAdvert", 
             BindingFlags.Instance | BindingFlags.NonPublic, 
             null, 
             new Type[] { item.GetType() }, 
             null); 
    return (bool)method.Invoke(this, new object[] { item }); 
} 

protected bool ValidAdvert(Derived1 item) 
{ 
    return ADerived1SpecificPredicate; 
} 

protected bool ValidAdvert(Derived2 item) 
{ 
    return ADerived2SpecificPredicate; 
} 

Эта модель называется MultipleDispatch, при вызове метода при отражении происходит медленнее, чем вызов метода напрямую (это не будет накладных расходов, если метод вызывается меньше, чем несколько сотен раз в секунду), то есть преимущество не требовать изменения иерархии классов, как в шаблоне Double Dispatch.

Это будет еще проще, когда C# 4.0 поставляется с динамическим ключевым словом:

protected bool ValidAdvert(Base item) 
{ 
    if (item.GetType() == typeof(Base)) 
     throw new ThisIsAnAbstractClassException(); 

    dynamic dynamicThis = this; 

    return (bool)dynamicThis.ValidAdvert(item as dynamic); 
} 
+0

Также умный и избегает возиться с различными производными классами (я бы хотел, чтобы их было всего 2!). –

+0

довольно примечания: вам нужно отбрасывать bool по возврату, а метод. Invoke берет [] объектов, и поэтому дерьмо, если вы попытаетесь пройти только 1 :) Спасибо, хотя! –

+0

@Ed - Я считаю, что сохранение предиката с производным классом на самом деле является предпочтительным методом. Хранение производной логики класса в базовом классе создает обратную связь между ними. Я не вижу в этом ничего хорошего. Базовый класс не должен знать ничего о своих производных классах. – tvanfosson

5

идеальное место для использования double dispatch:

class Base 
{ 
    public abstract bool IsValid(IAdvertValidator validator); 
} 

class Derived1 : Base 
{ 
    public bool IsValid(IAdvertValidator validator) 
    { 
     return validator.ValidAdvert(this); 
    } 
} 

class Derived2 : Base 
{ 
    public bool IsValid(IAdvertValidator validator) 
    { 
     return validator.ValidAdvert(this); 
    } 
} 

interface IAdvertValidator 
{ 
    bool ValidAdvert(Derived1 d1); 
    bool ValidAdvert(Derived2 d2); 
} 
+0

Clever, мне это нравится. –

+0

Разумное использование рисунка. Альтернативой было бы выставить методы проверки из валидатора и использовать их в реализации IsValid, сохраняя фактический поток проверки контроля в классе. – tvanfosson

0

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

+0

Да, это всегда один из производных классов, однако он также технически является базовым классом, то есть метод базового класса вызывается, когда я делаю (List ) i.Where (ValidateAdvert), например. –

+0

А - Я читал ваш образец кода, а не ваш вопрос. Вы на самом деле не хотите, чтобы он выдавал исключение, вы хотите, чтобы он вызывал один из других методов. В таком случае я бы пошел с предложением Антона или что-то подобное. Обратите внимание, что вы можете предоставить валидатор в конструкторе, чтобы вам не нужно передавать его в качестве аргумента. Я также использовал класс статической проверки, который проверяет простые свойства, устраняя необходимость хранить ссылку на него. Вы должны быть осторожны с ними, хотя, поскольку они могут сделать тестирование сложнее, если вы не будете осторожны. – tvanfosson

+0

Валидатор - это очень специфичный для страниц и статуса класс, и поэтому он не будет работать как статический, но я думаю, что он будет очень хорошо отражать метод отражения, это позор, который не поддерживается по умолчанию хоть! –

Смежные вопросы