8

「IFoo」を実装する基本クラス「MyClass」にアクセスできるとします。「IFoo」は関数「int FooValue()」を定義し、「MyClass」はそれを明示的に実装します。ここで、'MySubClass' という 'MyClass' のサブクラスがあり、そのサブクラスで 'FooValue' をオーバーライドしたいが、サブクラスの実装が基本クラスの実装の結果に基づくようにしたいとします。

通常、これは実装を基本クラスの保護された関数に移動するだけで解決され、それをサブクラスでオーバーライドするだけです。やった、やった。しかし、基本クラスのソース コードにはアクセスできません。ライブラリへの参照としてのみ使用します。では、これをどのように解決しますか?

重複ではありません (更新: ...as this one)!

ここにこの SO の質問があります... C#: インターフェイスを明示的に指定することによるプロパティのオーバーライド... これは、通常のチャネル自体を介して基本クラスのインターフェイスをオーバーライドすることはできませんが、同じインターフェイスを明示的に再実装できることを示していますサブクラスであり、インターフェイスをオーバーライドしているように動作します (ただし、実際には、オーバーライドするのではなく、再実装しています)。(そのため、これはその質問の複製ではありません。)

これは基本クラスの疑似コードですが、コード単位でアクセスすることはできません...

public interface IFoo
{
    int FooValue();
}

public class MyClass : IFoo
{
    int IFoo.FooValue() <-- Explicit implementation requiring a cast to access.
    {
        return 4;
    }
}

これは私たちがやろうとしていることですが、このように「ベース」を使用できないため、明らかにこれは許可されていません。

public class MySubClass : MyClass
{
    int IFoo.FooValue()
    {
        int baseResult = ((IFoo)base).FooValue(); <-- Can't use 'base' like this
        return baseResult * 2;
    }
}

それで、これは可能ですか?

4

2 に答える 2

3

正直なところ、これに対する簡単な答えはありません。言語の制限のように感じます。それが欠けているのには、何らかの正当な理由があるかもしれません。

ただし、あまりきれいではない回避策をいくつか考えることができます。

  1. 反射。私見、ここで最も簡単なオプションです。リフレクションが本当に必要な稀なケースの 1 つです。

  2. 参照されたライブラリから派生した独自のインターフェイスと基本クラス。

    //your interface
    public interface IRealFoo : IFoo
    {
        new int FooValue();
    }
    
    //your base class
    public class MyRealClass : MyClass, IRealFoo
    {
        protected virtual int FooValue()
        {
            return ((IFoo)this).FooValue();
        }
    
        int IRealFoo.FooValue()
        {
            return FooValue();
        }
    }
    
    //your child class
    public class MyRealSubClass : MyRealClass
    {
        protected override int FooValue()
        {
            return base.FooValue() * 2;
        }
    }
    

    そして、などの代わりにIRealFoo、などを扱います。MyRealClassIFooMyClass

    IRealFoo x = new MyRealClass();
    IRealFoo y = new MyRealSubClass();
    Console.WriteLine(x.FooValue()); //4
    Console.WriteLine(y.FooValue()); //8
    
  3. 上記と同じですが、インターフェイスではなく抽象クラスです。

    RealFoo上記と同じですが、 interface の代わりに抽象基本クラスを使用することもできますIFoo。これは少し簡単なコードだと思いますが、良いコードである必要はありません。コードの意図を完全に変更します。

    public abstract class RealFoo : MyClass
    {
        public virtual int FooValue()
        {
            return ((IFoo)this).FooValue();
        }
    }
    
    public class MyRealClass : RealFoo 
    {
        public override int FooValue() 
        {
            return base.FooValue();
        }
    }
    
    public class MyRealSubClass : MyRealClass 
    {
        public override int FooValue() 
        {
            return base.FooValue() * 2;
        }
    }
    
    //call it like:
    RealFoo x = new MyRealClass();
    RealFoo y = new MyRealSubClass();
    Console.WriteLine(x.FooValue()); //4
    Console.WriteLine(y.FooValue()); //8
    
  4. dynamicとともに拡張メソッド。

    public class MyRealClass : MyClass 
    {
        public virtual int FooValue() 
        {
            return ((IFoo)this).FooValue();
        }
    }
    
    public class MyRealSubClass : MyRealClass 
    {
        public override int FooValue() 
        {
            return base.FooValue() * 2;
        }
    }
    
    public static int RealFooValue(this IFoo foo) 
    {
        return ((dynamic)foo).FooValue();
    }
    

    この 1 つのケースでは、使い慣れたインターフェイスをそのまま使用できますが、 の代わりにIFoo拡張メソッドを呼び出す必要があります。これは、 を呼び出したときに誤った結果になる可能性があり、混乱を招く可能性があります。私はそれをお勧めしません。RealFooValueFooValueFooValue

    IFoo x = new MyRealClass();
    IFoo y = new MyRealSubClass();
    Console.WriteLine(x.RealFooValue()); //4
    Console.WriteLine(y.RealFooValue()); //8
    
  5. if-elseロジック付スイッチオンタイプ

    public class MySubClass : MyClass
    {
    
    }
    
    public static int RealFooValue(this IFoo foo) 
    {
        var type = foo.GetType();
    
        if (type == typeof(MyClass))
            return foo.FooValue();
        else if (type == typeof(MySubClass))
            return foo.FooValue() * 2; //logic goes here
    
        throw new Exception();
    }
    

    これには、上記と同じ問題があります。お勧めしません。

    IFoo x = new MyClass();
    IFoo y = new MySubClass();
    Console.WriteLine(x.RealFooValue()); //4
    Console.WriteLine(y.RealFooValue()); //8
    
于 2013-10-18T16:07:09.263 に答える
0

明示的なインターフェイスの実現は、IFoo.FooValue() がプライベートである ことを意味します (リフレクションによって確認できます)。

  MethodInfo mi = typeof(MyClass).GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Where(m => m.Name.EndsWith("IFoo.FooValue")).ToList()[0];

  if (mi.IsPrivate) {
    // And it is private.... 
  } 

したがって、継承された IFoo.FooValue() を呼び出すことはできません。

可能な迂回路

  public interface IFoo
  {
      int FooValue();
  }

  public class MyClass : IFoo
  {
      // This (main logic) should be inherited/override 
      protected virtual int CoreFooValue() 
      {
          return 4;
      }

      // Just a non-virtual interface method which is immutable
      int IFoo.FooValue() 
      {
          return CoreFooValue();
      }
  }

  public class MySubClass : MyClass {
      // Logic is changed, interface is not 
      protected override int CoreFooValue() 
      {
          return base.CoreFooValue() * 2;
      }
  }

非仮想インターフェイス パターンも参照してください。

http://en.wikipedia.org/wiki/Non-virtual_interface_pattern

于 2013-07-25T06:11:02.327 に答える