从具有继承接口的父实现继承接口

时间:2016-01-21 20:50:54

标签: c# inheritance interface

假设我有以下3个接口(格式设计为简洁):

public interface ISecondary { }

public interface ITertiary { }

public interface IRoot
{
    public ISecondary secondary
    public ITertiary tertiary
}

我有继承各自接口的二级和三级类(为简洁起见,格式化):

public class Secondary : ISecondary { }

public class Secondary2 : ISecondary { }

public class Tertiary : ITertiary { }

public class Tertiary2 : ITertiary { }

我希望能够将完善的类转化为可以触及两个接口的根类,例如:

public class Root : IRoot
{
    public Secondary secondary { get; set; }
    public Tertiary tertiary { get; set; }
    public string foo { get; set; } // This wouldn't need to be cross-compatible
}

public class Root2 : IRoot
{
    public Secondary2 secondary { get; set; }
    public Tertiary2 tertiary { get; set; }
    public double bar { get; set; } // This wouldn't need to be cross-compatible either
}

我想做一些事情(对不起,反思不是我的强项):

Root root = new Root();
// data is entered

Root2 root2 = new Root(IRoot root)

// Similar to how ObservableCollection can retrieve data
// from a List of the same generic type. This constructor
// would take data from the IRoot properties and put it into root2. 

所以IRoot的所有成员都从root转移到root 2。

在C#中执行此操作将导致匹配类型错误(CS0738)。

换句话说(如果这很难理解我会道歉)我希望我的root接口使用我的IRoot接口将具有的HAS-A关系中的二级和三级接口的签名,但是在实现时Root类,我也想实现它们各自的继承类,而不必使用实际的接口。

非常感谢你的时间。

2 个答案:

答案 0 :(得分:2)

由于Root实现IRootIRoot需要接口类型的属性,必须将这些属性声明为Root中的接口。

public class Root : IRoot
{
    public ISecondary secondary { get; set; }
    public ITertiary tertiary { get; set; }
}

这仍然允许您为它们分配类

IRoot root = new Root {
    secondary = new Secondary(),
    tertiary = new Tertiary()
};

如果由于某种原因,您想要访问不属于Secondary界面的ISecondary对象的成员,您可以这样做:

var secondary = root.secondary as Secondary;
if (secondary != null) {
    secondary.DoSomething();
}

另一种方法是使用泛型和泛型类型约束

public interface IRoot<TSecondary, TTeritary>
    where TSecondary : ISecondary
    where TTeritary : ITertiary
{
    public TSecondary secondary
    public TTeritary tertiary
}

然后你可以像这样声明两个根类

public class Root : IRoot<Secondary, Tertiary> 
{
    public Secondary secondary { get; set; }
    public Tertiary tertiary { get; set; }
}

public class Root2 : IRoot<Secondary2, Tertiary2> 
{
    public Secondary2 secondary { get; set; }
    public Tertiary2 tertiary { get; set; }
}

但是,请注意这两个类不兼容。优点仅在于强制执行由接口定义的某种结构。如果您确实需要赋值兼容性,请删除setter并使用out修饰符:

public interface IRoot<out TSecondary, out TTeritary>
    where TSecondary : ISecondary
    where TTeritary : ITertiary
{
    TSecondary secondary { get; }
    TTeritary tertiary { get; }
}

现在你可以这样做

var list = new List<IRoot<ISecondary, ITertiary>>();
list.Add(new Root());
list.Add(new Root2());

答案 1 :(得分:1)

好吧,要实现接口,您必须使用相同的类型,但是您可以保留更具体的实现并明确地实现接口属性:

public class Root : IRoot
{
    public Secondary secondary { get; set; }
    public Tertiary tertiary { get; set; }
    public string foo { get; set; } // This wouldn't need to be cross-compatible

    ISecondary IRoot.secondary {get {return this.secondary;} set{;}}
    ITertiary IRoot.tertiary {get {return this.tertiary;} set{;}}
}

这样,您可以在不进行强制转换的情况下以类型安全的方式使用更多特殊类型。但是,问题出在secondarytertiary setter 中。由于您可以传递任何 ISecondary,因此无法保证传入的对象是Secondary,因此您无法安全地将其分配给secondary属性。您的选择是:

  • 转换为Secondary,如果不是那种类型会抛出异常
  • 确定
  • Secondary get-only
  • 中创建属性