树中的泛型
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
}
我有一个段 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
}