树中的泛型

Generics in a tree

我有一个段 class,它是树中的一个节点。没有 Tree class。这棵树只是由片段组成。

public abstract class Segment 
{
    public List<Segment> Descendants { get; } => new List<Segment> Descendants;
    public abstract SegmentVisual VisualRepresentation { get; }
}

SegmentVisual 是一个 class 用于将段绘制到屏幕上。两个 class 都是抽象的,因为我有不同类型的线段,每个线段都需要以不同的方式绘制。

下面是 SegmentVisual 的样子:

public abstract class SegmentVisual : DrawingVisual 
{
    protected SegmentVisual(Segment owner){
        this.Owner = owner;
    }

    public Segment Owner { get; }
    public List<SegmentVisual> Descendants { get; } = new List<SegmentVisual>();

    public void Redraw()
    {
        this.RedrawItself();

        foreach (SegmentVisual visual in this.Visuals)
        {
            visual.Redraw();
        }
    }

    public abstract void RedrawItself();
}

就像段一样,视觉对象也有后代,因此可以将单个视觉对象添加到屏幕以绘制自身及其所有后代

这是一个实现:

public class LineSegment : Segment
{
    public LineSegment()
    {
        this.VisualRepresentation = new LineSegmentVisual(this);
    }

    public override SegmentVisual VisualRepresentation { get; }

    public Pen Stroke { get; set; }
}

.

public class LineSegmentVisual : SegmentVisual
{
    public LineSegmentVisual(LineSegment owner) // Resharper suggests this can be Segment base class
        : base(owner)
    {
    }

    public override void RedrawItself()
    {
        using (DrawingContext ctx = this.RenderOpen())
        {
            var owner = (LineSegment)this.Owner;
            ctx.DrawLine(owner.Stroke, this.Owner.Position, this.Owner.ControlPointPos);
        }
    }
}

我的问题是最后一个 class。你可以看到 ReSharper 建议。而且我一直对向下铸造感觉不太好。如果我必须在其他地方取回所有者,我将不得不低头再次获得 Stroke 属性。

如果我将 SegmentVisual 设为泛型 SegmentVisual<T>,其所有者为 T,则会引入新的障碍,因为现在 SegmentVisual 只能包含 [= 的后代18=] 不应该是这种情况,因为我希望它包含任何类型的 SegmentVisual,我只希望所有者是强类型的。

我只是希望 SegmentVisual 能够对应一个特定的 class 以便它的 Owner 属性 是强类型的。我似乎无法弄清楚这一点。

您可以使用 new 在您的子类中声明强类型 Owner:

public class LineSegmentVisual : SegmentVisual
{
    new LineSegment Owner { get { return (LineSegment)base.Owner; } }

    public LineSegmentVisual(Segment owner)
        : base(owner)
    {
    }

    public override void RedrawItself()
    {
        using (DrawingContext ctx = this.RenderOpen())
        {
            var owner = this.Owner;
            ctx.DrawLine(owner.Stroke, this.Owner.Position, this.Owner.ControlPointPos);
        }
    }
}

这样每次调用 this.Owner 时都会强制转换 base.Owner,但至少可以避免重复代码。


第二种方式是使用继承。声明 SegmentVisual 具有基本功能

abstract class SegmentVisual
{
    public List<SegmentVisual> Descendants { get; private set; }

    ...
}

OwnedSegmentVisual 具有强类型所有者

abstract class OwnedSegmentVisual<TOwner>: SegmentVisual where TOwner: Segment
{
    public TOwner Owner { get; private set; }

    protected OwnedSegmentVisual(TOwner owner)
    {
        Owner = owner;
    }
}

Owner 可以在子类中使用而无需强制转换,常用功能只需使用 SegmentVisual.


第三种方法是使用泛型协变,但你必须为你的类型声明接口:

public class Program
{
    public static void Main()
    {
        var sv = new LineSegmentVisual();
        sv.Descendants = new List<ISegmentVisual<Segment>> { new SquareSegmentVisual() };
    }
}


abstract class Segment {}

class LineSegment : Segment {}

class SquareSegment: Segment {}

interface ISegmentVisual<out TOwner>
{
    TOwner Owner { get; }

    List<ISegmentVisual<Segment>> Descendants { get; }
}

class LineSegmentVisual : ISegmentVisual<LineSegment>
{
    public LineSegment Owner { get; set; }
    public List<ISegmentVisual<Segment>> Descendants { get; set; }
}

class SquareSegmentVisual : ISegmentVisual<SquareSegment>
{
    public SquareSegment Owner { get; set; }
    public List<ISegmentVisual<Segment>> Descendants { get; set; }
}

希望这会有所帮助。

C# 不支持 return 类型变化。也就是说,以下是不可能的:

class Foo
{
    virtual Base Owner{ get; }
}

class Bar: Foo
{
    override Derived Owner { get; } // where Dervided: Base
}

您将收到一个编译时错误,通知您 Derived 没有覆盖任何合适的方法。老实说,我发现 C# 中缺少此功能有点烦人,尽管它在未来的版本中已经上线了几次,但从未成功。老实说,竞争功能已经更新,变得更加有趣和有用。

IMO 你在这里有两个合理的选择。一种是隐藏Owner:

class Bar: Foo
{
    new Derived Owner { get { return base.Owner as Derived; } // where Derived: Base
}

另一个正在使用显式实现的接口:

interface IOwnable
{
    Base Owner { get; }
}

class Foo: IOwnable
{
    Base IOwnable.Owner { get; }
}

class Bar: Foo
{
    Derived Owner { get { return ((IOwnable)base).Owner as Derived; } // where Derived: Base
}