繁体   English   中英

如何组织相互继承但又具有相互继承的属性的C#类?

[英]How do I organize C# classes that inherit from one another, but also have properties that inherit from one another?

我有一个具有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 ,这是一个具有坡度和特定种类的VenuePartVenue ,该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 ,而实际上它应该只有一个。 我不能覆盖它,因为类型不同,对吗? 运行报表时,我想VenuePart地引用这些类,在这些类中,我只吐出了VenueVenuePart 但是,当我渲染表格等时,我想具体一点。

我有很多这样的关系,想知道我做错了什么。 例如,我有一个OrderOrderItem S,也特定种类的Order S作特定种类的OrderItem秒。

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

更新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>
{
}

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); }
}

您可以使用协方差

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

我期待其他人的建议-但我的方法是在这种情况下使用泛型。 使用泛型,您的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;}}
}

如果删除两个集合的“集合”部分,则将更有意义:基类提供“所有部分”集合,而派生类除了具有基类一外,还具有过滤视图。

注意:根据您的需要,可能使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() };

GolfCourseGolfCourseOtherPlace没有共同的父类(对象除外),因此没有接口,就不能互换使用它们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM