【问题标题】:Trying to understand C# nested interfaces试图理解 C# 嵌套接口
【发布时间】:2021-04-14 13:33:35
【问题描述】:

我正在尝试获取一组简单的嵌套接口,以便我可以在派生类上强制执行一些成员。

 public enum Category { Mental, Physical, Technical }

        interface IAbilities
        {
            List<IAbility> Abilities { get; set; }
        }

        interface IAbility
        {
            Category Category { get; }
            int Value { get; set; }
            string Descritpion { get; set; }
        }

        public class MentalAbilities : IAbilities
        {
            // This is what I want so that "Abilities" is accessible
            public List<MentalAbility> Abilities { get; set; }

            // This is what Intellisense generates, cannot be set to public
            //List<IAbility> IAbilities.Abilities { get; set; }
        }

        public class MentalAbility : IAbility
        {
            public Category Category { get; } category
            public int Value { get; set; }
            public string Descritpion { get; set; }
        }

当然,Intellisense 生成的位编译,但“能力”不能从类实例访问,因为它不能设置为公共。

我想告诉我“MentalAbilities”没有实现接口成员“IAbilities.Abilities”。 “MentalAbilities.Abilities”无法实现“IAbilities.Abilities”,因为它没有 List 的匹配返回类型。

我不明白,因为“MentalAbility”是从“IAbility”接口派生的,所以应该履行合同。

【问题讨论】:

  • 这里没有嵌套任何东西。嵌套意味着在一个类中被删除。你可以在一个类中声明枚举、委托、接口、类,如此嵌套。但是在这里,没有任何东西是嵌套的。并且接口不能在接口中声明。
  • @OlivierRogier 好的,我明白了。我认为嵌套意味着另一个接口中的接口。所以我的 List is IAbility 是不可能的吗?我已将接口更改为公开,但它并没有解决我的问题。

标签: c# nested


【解决方案1】:

(我跳过了“x 比 y 更难访问”的错误,因为你已经知道你可以公开你的接口 - 确保你能理解任何东西必须至少与它的使用一样可访问)

您的“未实施”错误:

我理解你的问题,但你有点颠倒了

我看到你想确保,在你的 MentalAbilities.Abilities 列表中,有人只放置 MentalAbility 对象.. 他们实现了 IAbility 所以它应该满足“对象是一个 IAbility 并且有一个列表只包含实现 IAbility 的东西" 规则,对吧?

唉,不。这与继承或接口实现的用途几乎相反。

继承背后的想法是你可以说“这个东西可以是任何类型,只要它有这些共同的方面,我会以共同的方式对待它”。通过声明List&lt;IAbility&gt;,您是在说“此列表中的对象只需实现 IAbility,然后我就可以处理它们”——它可能是 MentalAbility 或 PhysicalAbility——没关系

然后您不能通过继承/实现机制将列表限制为仅包含 MentalAbility,您也不应该因为它与从“我可以处理任何事情只要它实现 X”到“我只有当它是或是 Y 的子类型时才会处理某些东西”与之前声称的相反。你是说 MentalAbility 只能处理列表内容,如果它们是 MentalAbility,当你之前说它可以处理任何是 IAbility 时

如果您想强制 MentalAbility 仅包含 MentalAbility,您必须将其声明为 List&lt;IAbility&gt; 并查看您在集合中获得的 IAbility 的类型,如果它不是 MentalAbility,则拒绝添加它,但是如果其他开发人员使用您的代码,他们会非常困惑 - “什么?它说我可以使用任何实现 IAbility 的东西,而我的 TigerAbility 就是这样做的。为什么我得到一个异常,它必须是 MentalAbility ?如果我不允许将任何我喜欢的 IAbility 放入其中,为什么这个开发人员甚至使用 List(IAbility)? - 它也不再是编译时的事情,而是运行时的事情,这是一个很大的风险

我认为您需要决定是否能够以通用方式处理这些事情,即您的 AbilitiesProcessor 是否可以实现 IAbility 并拥有一个 IAbility 列表并不管实际类型如何处理它们。如果它不能,并且您将拥有一个只能处理 MentalAbility 的 MentalAbility 类,这很好,并且它可以有一个 List&lt;MentalAbility&gt; 它想要的所有东西,其他开发人员不会错误地将 TigerAbility 放入其中,但是它不会实现 IAbilities 接口

【讨论】:

  • 好的,这很清楚,谢谢。我会找到更好的方法来做到这一点。我没有完全理解接口的用途。
【解决方案2】:

我正在尝试获取一组简单的嵌套接口,以便我可以在派生类上强制执行一些成员。

您的主要问题是默认将两个接口定义为private。这就是阻止您在 MentalAbilities 中创建 public List&lt;IAbility&gt; Abilities { get; set; } 的原因。

实际上,public List&lt;MentalAbility&gt; Abilities { get; set; } 属性也阻止了您,因为您不能将同名的属性定义两次。

这里最大的问题是允许MentalAbilities 设置List&lt;IAbility&gt; 的能力有什么意义,因为这意味着设置任何类型的能力。

理想情况下,MentalAbilities 应该只有一个包含 MentalAbility 的列表 - 并且它应该是只读的。事实上,您的大部分界面都应该是只读的。

这是你应该做的:

从这些接口开始:

public interface IAbilities<A> where A : IAbility
{
    List<A> Abilities { get; }
}

public interface IAbility
{
    Category Category { get; }
    int Value { get; }
    string Description { get; }
}

注意IAbilities&lt;A&gt; 接口使用了一个泛型类型A,您可以稍后填写。

接下来,让我们设置一些基类来简化每个特定类的实现。

public abstract class AbilitiesBase<A> : IAbilities<A> where A : IAbility
{
    public List<A> Abilities { get; protected set; }
}

public abstract class AbilityBase : IAbility
{
    public abstract Category Category { get; }
    public abstract int Value { get; }
    public abstract string Description { get; }
}

现在最后的类很简单:

public class MentalAbilities : AbilitiesBase<MentalAbility> { }

public class MentalAbility : AbilityBase
{
    public override Category Category => Category.Mental;
    public override int Value => 42;
    public override string Description => "Mental!";
}

MentalAbilities 中没有什么要实现的 - 基类负责处理 - 但它确实有一个 List&lt;MentalAbility&gt; Abilities { get; } 属性。

MentalAbility 也被巧妙地设置为强制您覆盖属性。

现在,如果您仍然想要一个 List&lt;IAbility&gt; Abilities { get; } 属性,那么通过编写以下两个接口可以轻松实现:

public interface IAbilities
{
    List<IAbility> Abilities { get; }
}

public interface IAbilities<A> : IAbilities where A : IAbility
{
    new List<A> Abilities { get; }
}

这会强制更改为 AbilitiesBase,如下所示:

public abstract class AbilitiesBase<A> : IAbilities<A> where A : IAbility
{
    public List<A> Abilities { get; protected set; }
    List<IAbility> IAbilities.Abilities => this.Abilities.Cast<IAbility>().ToList();
}

List&lt;IAbility&gt; IAbilities.Abilities 属性会生成public List&lt;A&gt; Abilities { get; protected set; } 属性的副本,但这就是您想要防止任何人将错误类型的能力添加到原始列表的原因。

就个人而言,我会选择这个:

public interface IAbilities
{
    IEnumerable<IAbility> Abilities { get; }
}

public interface IAbilities<A> : IAbilities where A : IAbility
{
    new List<A> Abilities { get; }
}

public abstract class AbilitiesBase<A> : IAbilities<A> where A : IAbility
{
    public List<A> Abilities { get; protected set; }
    IEnumerable<IAbility> IAbilities.Abilities => this.Abilities.Cast<IAbility>();
}

【讨论】:

    【解决方案3】:

    你所坚持的是 C# 中接口的 implicitexplicit 实现。如果你这样定义你的班级成员:

    public List<MentalAbility> Abilities { get; set; }
    

    你正在隐式实现IAbilities,你需要用它写public。如果您将班级成员定义为

    List<IAbility> IAbilities.Abilities { get; set; }
    

    你已经明确实现了IAbilities,在这种情况下它可以是私有的。

    虽然我们在这里,但我认为定义接口 IAbilities 没有意义。你应该在任何你想使用IAbilities的地方简单地使用IEnumerable&lt;IAbility&gt;,这实际上在你的代码中没有任何价值。

    【讨论】:

    • 使用public List&lt;MentalAbility&gt; Abilities { get; set; } 会产生帖子下方提到的错误:''MentalAbilities.Abilities' cannot implement 'IAbilities.Abilities' because it does not have the matching return type of List&lt;IAbility&gt;.
    猜你喜欢
    • 2020-02-13
    • 1970-01-01
    • 2020-08-18
    • 2017-05-24
    • 2017-07-08
    • 2021-09-20
    • 2011-12-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多