如何组织相互继承的C#类,同时也具有相互继承的属性?

如何组织相互继承的C#类,同时也具有相互继承的属性?,c#,entity-framework,oop,C#,Entity Framework,Oop,我有一个应用程序,它有一个场所的概念,一个事件发生的地方。一个场馆有许多VenueParts。所以,看起来是这样的: public abstract class Venue { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<VenuePart> VenueParts { get; set; } } public abstract

我有一个应用程序,它有一个
场所的概念,一个事件发生的地方。一个
场馆
有许多
VenuePart
s。所以,看起来是这样的:

public abstract class Venue
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<VenuePart> VenueParts { get; set; }
}
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); }
}
将来,可能还会有其他类型的
场馆
都继承自
场馆
。他们可以添加自己的字段,并且始终具有自己特定类型的
VenuePart
s

以下是
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
s和
VenuePart
s。但是,当我呈现表格之类的东西时,我想说得具体一点

我有很多这样的关系,我想知道我做错了什么。例如,我有一个
订单
,它有
订单项
s,但也有特定种类的
订单
s,它有特定种类的
订单项
s

更新:我应该注意,这些类是实体框架代码优先的实体。我希望这不重要,但我想可能会。我需要以代码优先能够正确创建表的方式构造类。这看起来不像代码首先可以处理泛型。很抱歉,此实施细节妨碍了优雅的解决方案:/

更新2:有人链接到指向的搜索,这似乎是一种将子类型中的列表约束为给定子类型的方法。这看起来很有希望,但是这个人删除了他们的答案!有人知道我如何利用这些概念吗


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

这里有一个使用泛型的可能选项:

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>
{
}
公共抽象类VenuePart
{
公共抽象字符串名称描述符{get;}
}
公共类HoleVenuePart:VenuePart
{
公共字符串名称描述符{get{return“我是一个洞”;}
}
公共课堂场地T:VenuePart
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共虚拟公司公司{get;set;}
公共虚拟ICollection VenueParts{get;set;}
}
公共级高尔夫球场:场地
{
}
在这里,GolfCourseVenue拥有VenueParts集合,它可以包含HoleVenueParts或超级类HoleVenueParts。场馆的其他专业化将限制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
{
}
公共抽象类VenuePart
{
公共抽象字符串名称描述符{get;}
}
公共类HoleVenuePart:VenuePart
{
公共字符串名称描述符{get{return“我是一个洞”;}
}
公共课堂场地
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共虚拟公司公司{get;set;}
公共虚拟ICollection VenueParts{get;set;}
}
公共级高尔夫球场:场地
{
}
现在GolfCourseVenue拥有VenueParts集合,它可以包含VenueParts或超级类VenueParts。在这里,场馆的所有专业都可以包含任何类型的VenuePart,这些VenuePart可能合适,也可能不合适

在回答你关于协方差的评论时,我建议如下:

public abstract class Venue
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<VenuePart> VenueParts { get; set; }
}
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); }
}
公共抽象类VenuePart
{
公共抽象字符串名称描述符{get;}
}
公共类HoleVenuePart:VenuePart
{
公共重写字符串名称描述符{get{return“I'm a hole”;}
}
公众抽象课堂场地
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共抽象ICollection VenueParts{get;}
}
公共级高尔夫球场:场地
{
私人ICollection_holeVenueParts;
公共高尔夫球场(I收集部分)
{
_holeVenueParts=零件;
}
公共覆盖ICollection VenueParts
{ 
得到
{ 
//这里我们需要防止客户端添加
//将新的VenuePart添加到VenueParts集合。
//他们必须使用Add(HoleVenuePart)。
//不幸的是,只有接口是协变的,而不是类型。
返回新的只读集合(
_holeVenueParts.OfType().ToList());
} 
}
公共无效添加(HoleVenuePart){{u holeVenueParts.Add(part);}
}
您可以使用

公共抽象课堂场地
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共虚拟公司公司{get;set;}
公共虚拟IEnumerable VenueParts{get;set;}
}
公共级高尔夫球场:场地
{
公共字符串斜率{get;set;}
公共高尔夫球场()
{
List HoleVenueParts=新列表();
添加(新的HoleVenuePart());
VenueParts=HoleVenueParts;
}
}

假设HoleVenuePart是从VenuePart继承来的,我期待其他人的建议——但我的方法是在这种情况下使用泛型。使用泛型,您的
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; }
}
如果您删除这两个集合的“set”部分,那么它将更有意义:基类提供“all parts”集合,而派生类除了基类1之外还具有筛选视图

注意:根据您的需要,使GolfVenue成为
场馆
的专业化通用产品可能无法作为
场馆
使用,并且
场馆
将无法使用
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;}}
}
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; } 
} 
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() };