我的应用程序具有Venue
的概念,即事件发生的地方。 Venue
有很多VenuePart
个。所以,它看起来像这样:
public abstract class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<VenuePart> VenueParts { get; set; }
}
Venue
可以是GolfCourseVenue
,这是Venue
,其中有一个斜率和一种特定类型VenuePart
,称为HoleVenuePart
:
public class GolfCourseVenue : Venue
{
public string Slope { get; set; }
public virtual ICollection<HoleVenuePart> Holes { get; set; }
}
将来可能还有其他类型的Venue
都来自Venue
。他们可能会添加自己的字段,并且始终拥有自己特定类型的VenuePart
。
以下是VenuePart
类:
public abstract class VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public override string NameDescriptor { get { return "Hole"; } }
public int Yardage { get; set; }
}
我上面的声明似乎是错误的,因为现在我有一个GolfCourseVenue
有两个集合,真的应该只有一个集合。我无法覆盖它,因为类型不同,对吧?当我运行报告时,我想通常引用这些类,我只是吐出Venue
和VenuePart
。但是,当我渲染表格等时,我想具体。
我有很多像这样的关系,我想知道我做错了什么。例如,我的Order
有OrderItem
个,但也有特定种类的Order
具有特定种类OrderItem
s。
更新:我应该注意这些类是Entity Framework Code-First实体。我希望这没关系,但我想可能会这样。我需要以Code-First可以正确创建表的方式构造类。它看起来不像Code-First可以处理泛型。对不起,这个实现细节正在妨碍优雅的解决方案:/
更新2:有人链接到指向Covariance and Contravariance的搜索,这似乎是一种将子类型中的列表限制为特定子类型的方法。这似乎很有希望,但这个人删除了他们的答案!有没有人知道如何利用这些概念?
更新3:删除了子对象中的导航属性,因为它让人感到困惑,无法帮助描述问题。
答案 0 :(得分:2)
以下是使用泛型的一种可能选项:
public abstract class VenuePart
{
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public string NameDescriptor { get{return "I'm a hole venue"; } }
}
public class Venue<T> where T : VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<T> VenueParts { get; set; }
}
public class GolfCourseVenue : Venue<HoleVenuePart>
{
}
这里的GolfCourseVenue有VenueParts集合,可以包含HoleVenueParts或超类HoleVenueParts。 Venue的其他专业将限制VenueParts包含特定于该场地的VenueParts。
第二种可能性就像你拥有它一样
public abstract class VenuePart
{
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public string NameDescriptor { get{return "I'm a hole venue"; } }
}
public class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<VenuePart> VenueParts { get; set; }
}
public class GolfCourseVenue : Venue
{
}
现在,GolfCourseVenue有VenueParts系列,可以包含VenueParts或超级VenueParts。这里Venue的所有专业都可以包含任何类型的VenuePart,可能适合也可能不适合。
在回答你对协方差的评论时,我会提出这样的建议:
public abstract class VenuePart
{
public abstract string NameDescriptor { get; }
}
public class HoleVenuePart : VenuePart
{
public override string NameDescriptor { get{return "I'm a hole venue"; } }
}
public abstract class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public abstract ICollection<VenuePart> VenueParts { get; }
}
public class GolfCourseVenue : Venue
{
private ICollection<HoleVenuePart> _holeVenueParts;
public GolfCourseVenue(ICollection<HoleVenuePart> parts)
{
_holeVenueParts = parts;
}
public override ICollection<VenuePart> VenueParts
{
get
{
// Here we need to prevent clients adding
// new VenuePart to the VenueParts collection.
// They have to use Add(HoleVenuePart part).
// Unfortunately only interfaces are covariant not types.
return new ReadOnlyCollection<VenuePart>(
_holeVenueParts.OfType<VenuePart>().ToList());
}
}
public void Add(HoleVenuePart part) { _holeVenueParts.Add(part); }
}
答案 1 :(得分:1)
您可以使用Covariance
public abstract class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual IEnumerable<VenuePart> VenueParts { get; set; }
}
public class GolfCourseVenue : Venue
{
public string Slope { get; set; }
public GolfCourseVenue()
{
List<HoleVenuePart> HoleVenueParts = new List<HoleVenuePart>();
HoleVenueParts.Add(new HoleVenuePart());
VenueParts = HoleVenueParts;
}
}
假设HoleVenuePart继承自VenuePart
答案 2 :(得分:1)
我期待着其他人的建议 - 但我的方法是在这种情况下使用泛型。使用泛型,您的GolfCourseVenue
的“部分”是强类型的!
...当我输入这个时,其他所有人都在说泛型。您如何快速打包类型?!
无论如何,假装我还是第一次 -
public class VenuePart
{
}
public class HoleVenuePart : VenuePart
{
}
public abstract class Venue<T> where T : VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<T> Parts { get; set; }
}
public class GolfCourseVenue : Venue<HoleVenuePart>
{
public string Slope { get; set; }
}
此外,作为第二个选项,您也可以使用界面,因此如果您不喜欢名称Parts
,当已知派生类型时,您可以将其称为Holes
高尔夫球场
public class VenuePart
{
}
public class HoleVenuePart : VenuePart
{
}
public interface IPartCollection<T> where T : VenuePart
{
ICollection<T> Parts { get; set; }
}
public abstract class Venue<T> : IPartCollection<T> where T : VenuePart
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<T> Parts { get; set; }
}
public class GolfCourseVenue : Venue<HoleVenuePart>
{
public string Slope { get; set; }
ICollection<HoleVenuePart> IPartCollection<HoleVenuePart>.Parts { get { return base.Parts; } set { base.Parts = value; }}
public virtual ICollection<HoleVenuePart> Holes { get { return base.Parts; } set { base.Parts = value;}}
}
答案 3 :(得分:0)
如果删除两个集合的“set”部分比它更有意义:基类提供“所有部分”集合,而派生类除了基类1之外还具有过滤视图。
注意:根据您的需要,使得GolfVenue成为Venue<VenuePart>
的特化通用可能无法用作Venue<Type1>
,而Venue<Type2>
将无法使用任何良好的基类。
考虑使用接口而不是基类,因为它可以提供更多的实现灵活性。
public interface IVenue
{
public int Id { get; }
public string Name { get; }
public virtual IEnumerabe<VenuePart> VenueParts { get; }
}
public interface IGolfCourse : IVenue
{
public virtual IEnumerabe<HoleVenuePart> Holes { get; }
}
现在你可以使用GolfCourse:Venue来自其他样品,但由于它实现了界面,你也可以用大胆的方式处理它:
class GolfCourse:Venue<HoleVenuePart>, IGolfCourse {
public virtual IEnumerabe<VenuePart> Holes{ get
{
return VenueParts.OfType<HoleVenuePart>();
}
}
}
class OtherPlace:Venue<VenuePart>, IVenue {...}
List<IVenue> = new List<IVenue> { new GolfCourse(), new OtherPlace() };
Nothe GolfCourse
和OtherPlace
没有共同的父类(对象除外),所以没有界面就不能互换使用它们。