简体   繁体   中英

How to call a base function that's an explicit interface implementation?

Basic problem (pseudocode):

interface ISomethingDoer
{
   void DoSomething();
}

class A: ISomethingDoer
{
   void ISomethingDoer.DoSomething()
   {
      Something.Do();
   }
}

class B: A, ISomethingDoer
{
   void ISomethingDoer.DoSomething()
   {
      if (reason)
      {
         base.DoSomething(); //this does not compile
      }
      SomethingElse.Do();
   }
}

Is there any way to make this work without removing the explicit implementation from class A ?

I would suggest changing your base class a little such that DoSomething calls a protected method:

class A: ISomethingDoer
{
   void ISomethingDoer.DoSomething()
   {
       DoSomethingImpl();
   }

   protected void DoSomethingImpl()
   {
       Something.Do();
   }
}

And then in B you can call DoSomethingImpl :

class B: A, ISomethingDoer
{
   void ISomethingDoer.DoSomething()
   {
      if (reason)
      {
         DoSomethingImpl(); //this does compile
      }
      SomethingElse.Do();
   }
}

The alternative method suggested by Lasse V. Karlsen is to use reflection:

class B: A, ISomethingDoer
{
   void ISomethingDoer.DoSomething()
   {
      if (reason)
      {
         string baseName = $"{typeof(ISomethingDoer).FullName}.{nameof(DoSomething)}";
         MethodInfo baseMethod = this.GetType().BaseType
             .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
             .FirstOrDefault(m => m.IsPrivate && m.IsFinal && m.Name == baseName);
          baseMethod.Invoke(this, new object[0]);
      }
      SomethingElse.Do();
   }
}

But I don't like this approach since it uses reflection and is going to be slower. I used this answer to help me build the reflection solution.

You can use GetParameters() if you need to filter different overloads of the method, and you can specify arguments by building an object[] array containing them in the same positional order.

You aren't able to call base on an interface type, but you could refactor your classes to make this possible.

By moving the content of your method into a protected method, you can call it directly by both classes, like this:


interface ISomethingDoer
{
    void DoSomething();
}

class A : ISomethingDoer
{
    void ISomethingDoer.DoSomething()
    {
        _DoSomething();
    }

    protected void _DoSomething()
    {
        Something.Do();
    }
}

class B : A, ISomethingDoer
{
    void ISomethingDoer.DoSomething()
    {
        if (reason)
        {
            base._DoSomething();
        }
        SomethingElse.Do();
    }
}

This might be a good basic implementation:

public interface IAnimal
{
    void Eat();
}

public class Animal : IAnimal
{
    public void Eat()
    {
        Console.WriteLine("This Animal is Eating!");
    }
}

public class Shark : Animal
{
    public void Eat(bool useBase)
    {
        if (useBase)
        {
            base.Eat();
        }
        else
        {
            Console.WriteLine("This Shark is devouring everything with 120840128904 teeth");
        }
    }
}

I always make private implementation as wrappers to methods.

interface ISomethingDoer
{
    void DoSomething();
}

class A : ISomethingDoer
{
    protected void DoSomething()
    {
        Something.Do();
    }
    void ISomethingDoer.DoSomething() => DoSomething();
}

class B : A, ISomethingDoer
{
    protected void DoSomethingElse()
    {
        if (reason)
        {
            base.DoSomething();
        }
        SomethingElse.Do();
    }
    void ISomethingDoer.DoSomething() => DoSomethingElse();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM