我有一个细分类,它是树中的一个节点。没有Tree类。该树仅由段组成。

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


SegmentVisual是用于将细分绘制到屏幕上的类。这两个类都是抽象的,因为我有不同类型的线段,并且每个线段的绘制方式都不同。

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


我的问题是最后一堂课。您可以看到ReSharper的建议。而且我一直都不太满意投降。如果必须在其他地方检索所有者,则必须沮丧地重新获得Stroke属性。

如果我将SegmentVisual泛型SegmentVisual<T>的所有者设为T,则会引入新的障碍,因为现在SegmentVisual只能包含SegmentVisual<T>的后代,而事实并非如此,因为我希望它包含任何类型的SegmentVisual,我只希望所有者强类型化。

我只希望SegmentVisual能够对应于特定的类,以便其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);
        }
    }
}


这样,每次调用base.Owner时都会强制使用this.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; }
}




希望这会有所帮助。

09-26 14:32