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

本文关键字:继承 接口 实现 | 更新日期: 2023-09-27 18:31:19

假设我有以下 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 的所有成员都从根转移到根 2。

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

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

谢谢你的时间。

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

由于

Root实现IRoot并且IRoot需要接口类型的属性,因此必须在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()
};

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

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());

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

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二传手。 因为你可以传入任何ISecondary,所以不能保证传入的对象是Secondary,所以你不能安全地将它分配给secondary属性。 您的选择是:

  • 强制转换为 Secondary ,如果不是该类型,则会引发异常
  • 确定是否可以投射到Secondary,如果不能,请执行其他操作
  • 将属性设为IRoot仅获取