我是否需要在派生类的定义中指定由基类实现的接口?

时间:2017-02-02 15:04:24

标签: c#

在C#中,假设类C1实现接口I1,类C2从C1派生。

  1. 一般来说,在C2的定义中,我是否需要声明 那C2实现了I1?
  2. 同样的问题,如果在C1的声明中,I1有一个类型参数, C1和C2的类型参数不同?

    例如,

    public abstract class C1: I1<C1>
    
    public class C2: C1, I1<C2>
    

    我是否需要在C2的定义中编写I1?

  3. 感谢。

3 个答案:

答案 0 :(得分:4)

  1. 否。如果接口是在基类中实现的,派生类也会实现它们,而不必再次明确声明它。
  2. 必须明确声明不同通用参数的通用接口。原因是,例如IEnumerable<int>是一个完全不同的类型而不是IEnumerable<string>,它最有可能要求派生类实现具有与基类不同的签名的方法。
  3. 示例:

    public interface IGenericInterface<T> 
    {
         T GetSomething();
         void DoSomething(T arg);
    }
    
    public class BaseClass : IGenericInterface<int>
    {
         public virtual int GetSomething() { return 5; }
         public virtual void DoSomething(int arg) { Console.WriteLine(arg); }
    }
    
    public class Derived : BaseClass, IGenericInterface<string>
    {
         string IGenericInterface<string>.GetSomething() { return "hello world!"; }
         public void DoSomething(string arg) { Console.WriteLine(arg); }
    }
    

    请注意,在此示例中,Derived.GetSomething()必须为implemented explicitly,否则会与基类冲突。 int版本。具有相同名称的方法不允许的返回类型不同。

答案 1 :(得分:1)

如果基类实现了某个接口,则不必在派生类中重新实现它。

答案 2 :(得分:1)

  1. 考虑到正常使用案例,答案通常是否定的。但是有一种情况,你应该再次声明接口;当你想修改行为时。请考虑以下代码:

    public interface IHello
    {
        string SayHello();
    }
    
    public class Foo : IHello
    {
        public string SayHello() => "Foo says hello!";
    }
    
    public class DerivedFoo : Foo { }
    
    public class AnotherDerivedFoo : Foo, IHello
    {
        string IHello.SayHello() => "AnotherDerivedFoo says hello!";
    }
    

    现在:

    IHello foo = new Foo();
    IHello derivedFoo = new DerivedFoo();
    IHello anotherDerivedFoo = new AnotherDerivedFoo();
    
    Console.WriteLine(foo.SayHello()); //prints "Foo says hello!"
    Console.WriteLine(derivedFoo.SayHello()); //prints "Foo says hello!"
    Console.WriteLine(anotherDerivedFoo.SayHello()); //prints "AnotherDerivedFoo says hello!" !!!
    

    您的问题可能是指FooDerivedFoo 该语言的一个很少已知的功能是AnotherDerivedFoo,您基本上会以不同的行为重新实现该界面。

    请注意以下内容:

    var anotherDerivedFoo = new AnotherDerivedFoo(); //reference is typed AnotherDerivedFoo
    Console.WriteLine(anotherDerivedFoo.SayHello()); //prints "Foo says hello!"
    

    这是因为您无法重新实现接口的隐式实现。

  2. 是的,您必须同时申报。请注意,现在C2将实现 I1<C1>I1<C2>,一个不会取消另一个,它们是出于所有目的,两个不同的接口。

    这可能会导致不幸的情况:

    public interface I1<T>
    {
        string Foo(T fooable);
    }
    
    public class C1: I1<int>
    {
        public string Foo(int i) => "C1.Foo called";
    }
    
    public class C2: C1, I1<double>
    {
        public string Foo(double d) => "C2.Foo called";
    }
    

    现在:

    var c2 = new C2();
    Console.WriteLine(c2.Foo(1)); //C2.Foo(double d) is called,
                                        //not C1.Foo(int i)!
    

    所以要小心!