C # производные классы, разрешение перегрузки - PullRequest
5 голосов
/ 06 августа 2010

Хорошо, у меня есть несколько различных объектов, которые получены из базового класса, и я поместил их в список. Я хочу пройтись по списку и нажать каждый метод. У меня есть отдельные методы с сигнатурой каждого из них, но компилятор жалуется. Может кто-нибудь объяснить, почему? Это возможность использовать Generics, и если да, то как?

class Base { }
class Level1 : Base { }
class Level2 : Level1 { }

...

List<Base> oList = new List<Base>();
oList.Add(new Level1());
oList.Add(new Level2());

...

...
foreach(Base o in oList)
{
   DoMethod(o);
}

...

void DoMethod(Level1 item) { }
void DoMethod(Level2 item) { }

Что я делаю не так?

Ответы [ 8 ]

7 голосов
/ 06 августа 2010

Перегрузки разрешаются в время компиляции - и у вас нет DoMethod(Base item) метода - поэтому он не может разрешить вызов. Оставляя список и цикл из вещей, вы фактически пишете:

Base o = GetBaseFromSomewhere();
DoMethod(o);

Компилятор должен найти метод с именем DoMethod, который применим для одного аргумента типа Base. Такого метода не существует, следовательно, неудача.

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

  • Как говорит Маркос, вы можете использовать динамическую типизацию в C # 4, чтобы компилятор C # применял перегрузку во время выполнения , используя фактический тип объекта, к которому относится o.
  • Вы можете использовать Шаблон посетителя , чтобы эффективно получить двойную рассылку (мне это никогда не нравилось)
  • Вы можете использовать as или is:

    Level1 x = o as Level2;
    if (x != null)
    {
        DoMethod(x); // Resolves to DoMethod(Level1)
    } 
    else
    {
        Level2 y = o as Level2;
        if (y != null)
        {
            DoMethod(y); // Resolves to DoMethod(Level2)
        }
    }
    

    Опять же, это довольно уродливо

  • Переработайте то, что вы делаете, чтобы иметь возможность использовать нормальное наследование, если это возможно
2 голосов
/ 06 августа 2010

Какой метод вызывается, определяется во время компиляции, а не во время выполнения, поэтому компилятор не может знать, какой из них вызывать.У вас есть 2 варианта: переключить тип объекта и вызвать соответствующий метод, или, если вы используете .NET 4, использовать тип dynamic.

foreach(dynamic o in oList)
{
   DoMethod(o);
}
2 голосов
/ 06 августа 2010

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

Вы хотите использовать наследование и переопределение .

class Base { public virtual void DoMethod() { /* ... */  } }
class Level1 : Base { public override void DoMethod() { /* ... */ } }
class Level2 : Level1 { public override void DoMethod() { /* ... */ } }
1 голос
/ 06 августа 2010

Чтобы расширить ответ Марка, DoMethod должен быть виртуальным методом в Base, который вы вызываете для каждого элемента списка.

1 голос
/ 06 августа 2010

В вашем цикле foreach o имеет тип Base, и ни одна из перегрузок DoMethod не принимает экземпляр Base.Если возможно, вы должны переместить DoMethod в Base и переопределить его в двух подклассах:

public class Base
{
    public virtual void DoMethod() { ... }
}
1 голос
/ 06 августа 2010

У вас нет метода DoMethod (базовый элемент). Перегрузка не полиморфна. Обычно это делается с помощью виртуального метода:

class Base {
    public virtual void DoMethod() {...}
}
class Level1 : Base {
    public override void DoMethod() {...}
}
// etc..

foreach(Base o in oList)
{
    o.DoMethod();
}
0 голосов
/ 13 февраля 2019

Поскольку C # 7.0 сопоставление с образцом является еще одним вариантом.

Для получения дополнительной информации см. MSDN .Ваш код хотел бы:

switch(o)
{
    case Level2 level2: Do(level2); break;
    case Level1 level1: Do(level1); break;
    case Base @base: Do(@base); break;
    default: ...
    case null: ...
}
0 голосов
/ 06 августа 2010

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

Объявить интерфейс, реализовать его в каждом из ваших классовтогда вы сможете привести непосредственно к интерфейсу и запустить функцию оттуда.Мой C # немного шаткий, но что-то вроде

Interface IMethodizable
{
   void DoMethod();
}

class Level1 : IMethodizable {
  void DoMethod(){
    //insert code here
  }
}

class Level2 : IMethodizable {
  void DoMethod(){
    //insert code here
  }
}

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

...