Как я могу переопределить виртуальный метод, но по-прежнему вызывать версию базового класса в C # - PullRequest
0 голосов
/ 10 марта 2011

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

Например:

public class A {
    public virtual void Foo() {...}
}

public class B : A {
    public override void Foo() {...}
}

public class Program {
    public void SomeMethod()
    {
       ...

       //  ListofA is type IEnumerable<A>
       foreach (var item in ListofA)
       {
           // I want this to call A.Foo(), rather than B.Foo()
           // But everything I've tried, which has really just been casting, has resulted in B.Foo()
           item.Foo();
       }
    }
}

Ответы [ 8 ]

8 голосов
/ 10 марта 2011

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

3 голосов
/ 10 марта 2011

@ Джеймс прав.Чтобы основываться на его ответе, поскольку вы можете вызывать базовую версию из переопределенной, вы можете отправить какой-то флаг в метод, чтобы указать, выполнять ли его собственную реализацию или переопределенную.Примерно так:

public override void Foo(bool useBaseImplementation)
{
    if(useBaseImplementation)
    {
        base.Foo(useBaseImplementation);
    }
    else
    {
        //other stuff here
    }
}

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

1 голос
/ 10 марта 2011

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

public class A {
  public virtual void Foo() {...}
}

public class B : A {
  public override void Foo() {...}
  public override void parentFoo(){
    base.Foo();
  }
}

public class Program {
  public void SomeMethod(){
    ...

     //  ListofA is type IEnumerable<A>
     foreach (var item in ListofA){
       item.Foo(); //calls B.Foo()
       item.parentFoo(); //calls B.parentFoo() == A.Foo()
     }
  }
}
1 голос
/ 10 марта 2011

Если это так, то то, что вы хотите, на самом деле не является переопределением;Вы должны переосмыслить свой дизайн.Если вместо override вы используете new в переопределении, метод не будет полиморфным, но, опять же, это весьма показательно с ошибочным дизайном.Вам также понадобится foreach (A item in ListofA)

0 голосов
/ 10 марта 2011

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

   class Program
{
    static void Main(string[] args)
    {
        List<A> list = new List<A>
        {
            new B(),
            new B(),
            new B(),
            new B()
        };

        foreach (A a in list)
        {
            a.Foo();
        }

        foreach (B b in list)
        {
            b.Foo();
        }

        Console.ReadLine();
    }
}

public class A
{
    public void Foo()
    {
        Console.WriteLine("Base");
    }
}

public class B : A
{
    public new void Foo()
    {
        Console.WriteLine("Derived");
    }
}

Это вызовет реализацию B.Foo (), только если используемая вами ссылка имеет типB.Я бы не советовал вам делать это, поскольку я никогда не сталкивался с ситуацией, когда ключевое слово new имело бы смысл.

0 голосов
/ 10 марта 2011

Вы можете достичь этого, используя new вместо override.Здесь очень хорошо объяснено: http://msdn.microsoft.com/en-us/library/ms173153%28v=vs.80%29.aspx

0 голосов
/ 10 марта 2011

Вы можете вызвать базовую реализацию с помощью ключевого слова base ВНУТРИ класса, но если вы пытаетесь вызвать его из другого класса, я почти уверен, что вы нарушаете принцип подстановки Лискова в своем коде, как и любой подкласс. Такая реализация виртуальных методов должна быть полностью заменяющей суперкласс. Пересмотрите СВОЙ дизайн.

0 голосов
/ 10 марта 2011

Если var item = new A(), это вызовет A.Foo().

...