【问题标题】:Using stronger typed lists in derived objects than the parent has defined在派生对象中使用比父对象定义的更强的类型列表
【发布时间】:2015-11-10 09:38:55
【问题描述】:

信息: IDogShelter 和 ICatShelter 都继承自 IAnimalShelter。

接下来,我有实现和扩展 IAnimal 的 Cat 和 Dog 类。

IAnimalShelter has a List<IAnimal> called Animals.
ICatShelter has a List<Cat> called Animals.
IDogShelter has a List<Dog> called Animals.

问题: ICatShelter 不能有一个名为 Animals 的列表,因为 IAnimalShelter 将它定义为 IAnimal 的列表。 VS 说要使用“new”关键字。所以我只是把它拿走,在 IAnimalShelter 上没有 IAnimal 列表。

所以 - 我有一个名为 GetAnimalShelters() 的方法,它返回 IAnimalShelter 列表。然后我希望能够遍历收容所中的每个 IAnimal,但不能因为 IAnimalShelter 没有 IAnimal 列表。这让我不得不将我的结果转换为 ICatShelter 或 IDogShelter 以进行迭代,但我希望能够成功地将 IAnimal 列表添加到 IAnimalShelter 中。我该怎么做?

例如:

public interface IAnimalShelter
{
    List<IAnimal> Animals { get; set; }
}

public interface ICatShelter
{
    List<Cat> Animals { get; set; }
}

public interface IDogShelter
{
    List<Dog> Animals { get; set; }
}

然后:

List<IAnimalShelter> shelters = GetShelters(search);  //Works
foreach (IAnimalShelter shelter in shelters)
{
    var x = shelter.Animals;  
}

所以在上面,我只想访问动物的公共部分,我希望 IAnimalShelter 有一个 Animals 属性,但是对于派生的庇护所类型有更强类型的 Animals 列表。

【问题讨论】:

  • 我不明白 ICatShelter 和 IDogShelter 中需要 List&lt;Cat&gt;List&lt;Dog&gt;。就在您初始化Animals 列表时,只需将其设为new List&lt;Cat&gt;new List&lt;Dog&gt;
  • 我的理由是代码中还有很多其他地方知道他们正在处理 Cats,例如,因此希望能够访问 Cat.Whiskers 而无需将动物强制转换为 List .
  • 我最终还是选择了这个,因为事实证明,我需要访问界面之外的一两个特殊属性的时间很少。

标签: c# interface


【解决方案1】:

您的IAnimalShelter 类型承诺其Animals 属性将产生一个对象,其Add 方法将接受Animal 类型的参数。如果ICatShelter 派生自IAnimalShelter,它还必须有一个Animals 属性,该属性产生一个对象,其Add 方法将接受Animal 类型的参数。

要使这个模型工作,你应该定义IShelter&lt;out T&gt; where T:Animal,它包括一个Animals 类型的IReadOnlyList&lt;T&gt; 属性和一个TryDonate(Animal it) 方法,它将请求收容所接收特定的动物。 CatShelter : IShelter&lt;Cat&gt; 将能够让客户端代码接收一个可读的对象列表,这些对象保证都是Cat 类型,并且客户端可以尝试向它提交任何类型的动物,尽管捐赠类型不是@987654336 @ 不会被添加到列表中。

【讨论】:

  • 在评论时,我认为这是唯一解决需要遍历 IAnimals 并引用 IAnimalShelter 的答案,因为普通的旧 &lt;T&gt; 不会让你这样做方差。我认为通过分离TryDonate 方法可以改进这个答案,因为甚至不清楚提问者是否需要该功能并且它使它看起来更复杂。 (也许IEnumerable 也可以与IReadOnlyList 一起提及。)
【解决方案2】:

这称为泛型。以下定义应为您提供所需的内容:

public interface IAnimal { }

public class Cat : IAnimal { }

public class Dog : IAnimal { }

public interface IAnimalShelter<T> where T : IAnimal
{
    List<T> Animals { get; set; } 
}

public interface ICatShelter : IAnimalShelter<Cat> { }

public interface IDogShelter : IAnimalShelter<Dog> { }

ICatShelterAnimals 属性将为List&lt;Cat&gt;

像这样实现类,编译器很高兴:

public class CatShelter : ICatShelter
{
    public List<Cat> Animals { get; set; }
}

【讨论】:

    【解决方案3】:

    使用泛型:

    public interface IAnimalShelter<T> where T : IAnimal
    {
        List<T> Animals { get; set; }
    }
    
    public class Cat: IAnimal
    {
    }
    
    public interface ICatShelter : IAnimalShelter<Cat>
    {
    }
    
    public class CatShelter: ICatShelter
    {
        public List<Cat> Animals { get; set; }
    }
    

    没有对其进行测试,但这应该可以解决问题,如果您的 ICatShelter 没有向 IAnimalShelter 添加任何内容,您甚至可以删除它并直接从 IAnimalShelter 派生 CatShelter

    【讨论】:

      猜你喜欢
      • 2022-01-10
      • 2021-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-05
      相关资源
      最近更新 更多