如何组织彼此继承的C#类,还具有彼此继承的属性?

时间:2012-03-27 17:26:57

标签: c# entity-framework oop

我的应用程序具有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有两个集合,真的应该只有一个集合。我无法覆盖它,因为类型不同,对吧?当我运行报告时,我想通常引用这些类,我只是吐出VenueVenuePart。但是,当我渲染表格等时,我想具体。

我有很多像这样的关系,我想知道我做错了什么。例如,我的OrderOrderItem个,但也有特定种类的Order具有特定种类OrderItem s。

更新:我应该注意这些类是Entity Framework Code-First实体。我希望这没关系,但我想可能会这样。我需要以Code-First可以正确创建表的方式构造类。它看起来不像Code-First可以处理泛型。对不起,这个实现细节正在妨碍优雅的解决方案:/

更新2:有人链接到指向Covariance and Contravariance的搜索,这似乎是一种将子类型中的列表限制为特定子类型的方法。这似乎很有希望,但这个人删除了他们的答案!有没有人知道如何利用这些概念?

更新3:删除了子对象中的导航属性,因为它让人感到困惑,无法帮助描述问题。

4 个答案:

答案 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 GolfCourseOtherPlace没有共同的父类(对象除外),所以没有界面就不能互换使用它们。

相关问题