Подтвердить что ты не робот

Как определить, отменен ли виртуальный метод в С#

Можно ли определить, был ли переопределен виртуальный метод:

class ABase {

  public void DoSomething(object p)
  {
    p.Process();
    if( /* DoSomethingExtra is implemented */ )
      DoSomethingExtra(p);
  }
  public virtual void DoSomethingExtra(object p) { }

}

class ADerived {
  public override void DoSomethingExtra(object p)
  {
    p.ProcessMore();
  }
}

Я понимаю, что этот пример кажется глупым (например, почему бы вам просто не вызвать DoSomethingExtra(), поскольку он ничего не делает). Уверяю вас, у меня есть законное дело. Любые идеи?

4b9b3361

Ответ 1

Отметьте Обнаружение, если метод был переопределен с помощью Reflection (С#)

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

Ответ 2

Итак, в С# можно объявить виртуальный метод без его реализации.

Это невозможно. Вы можете объявить метод абстрактным, но если метод является виртуальным, он будет иметь некоторую реализацию (даже если реализация фактически является нулевым оператором).

В приведенном выше коде приведена ошибка Error 1 'ABase.DoSomethingExtra(object)' must declare a body because it is not marked abstract, extern, or partial.

Типичный способ справиться с этим состоит в том, чтобы просто объявить метод с реализацией null op и называть его:

class ABase {
  public void DoSomething(object p)
  {
    p.Process();
    DoSomethingExtra(p); // Always call
  }
  public virtual void DoSomethingExtra(object p)
  {
      // Do nothing here
  }
}

Изменить: теперь, когда ваш вопрос был отредактирован, я дам вам дополнительную информацию, связанную с вашим редактированием, в частности:

Я понимаю, что этот пример кажется глупым (например, почему бы вам просто не вызвать DoSomethingExtra(), поскольку он ничего не делает). Уверяю вас, у меня есть законное дело. Любые идеи?

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

Это, как говорится, я бы поставил под вопрос цель дизайна здесь. Ваш вопрос в основном требует определенного способа нарушить Принцип замены Лискова, который является одним из основных принципов объектно-ориентированного программирования. Это приведет к тому, что ваш код станет менее функциональным и будет гораздо менее ремонтопригодным...

Ответ 3

Отражение - хороший ответ на этот вопрос.

using System.Reflection;

Type classType = typeof(ADerived);
MethodInfo method = classType.GetMethod("DoSomethingExtra");
if (method.DeclaringType == typeof(ABase))
    Console.WriteLine("DoSomethingExtra not overridden.");
else
    Console.WriteLine("DoSomethingExtra is overridden by " + method.DeclaringType.Name);

Я надеюсь, что вы найдете это полезное.

Как только я реализовал специальный объект-просмотрщик, когда объект был неизвестен, я бы использовал ToString(), если он не был переопределен.

Ответ 4

Есть несколько вариантов:

Если производные классы должны реализовать DoSomethingExtra(), тогда объявите метод и класс как abstract. Это заставит конкретный производный класс иметь реализацию. Затем вы можете просто вызвать DoSomethingExtra() из кода базового класса, зная, что реализация будет существовать.

abstract class ABase {

  public void DoSomething(object p)
  {
    p.Process();
    DoSomethingExtra(p);
  }

  public abstract void DoSomethingExtra(object p);
}

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

Другим вариантом является наличие флага, который производные классы могут устанавливать, указывая, хотят ли они что-то дополнительное:

class ABase {

  public virtual bool ShouldDoSomethingExtra { get { return false; } }

  public void DoSomething(object p)
  {
    p.Process();
    if(ShouldDoSomethingExtra)
      DoSomethingExtra(p);
  }
  public virtual void DoSomethingExtra(object p) { // Empty in base }
}

class ADerived {
  public override void DoSomethingExtra(object p)
  {
    p.ProcessMore();
  }

  public override bool ShouldDoSomethingExtra { get { return true; } }
}

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

Ответ 5

Это самый простой способ получить то, что вы хотите:

var isDerived = typeof(ADerived).GetMember("DoSomethingExtra", 
                   BindingFlags.NonPublic 
                 | BindingFlags.Instance 
                 | BindingFlags.DeclaredOnly).Length == 0;

Обновление: Ссылка, представленная здесь, является более подробной, чем необходимо, но также неполной. Версия выше также будет работать для защищенных методов и виртуальных свойств (для чего вы используете GetMember вместо более ограничительного GetMethod), ни один из которых не имеет ссылок.