I have a segment class which is a node in the tree. There's no Tree
class. The tree is just made up of segments.
public abstract class Segment
{
public List<Segment> Descendants { get; } => new List<Segment> Descendants;
public abstract SegmentVisual VisualRepresentation { get; }
}
SegmentVisual
is a class used to draw the segment to the screen. Both classes are abstract because I have different types of segments and each of it needs to be drawn differently.
Here's how SegmentVisual
looks like:
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();
}
Just like the Segment, the Visual also has descendants so that a single visual can be added to screen to draw itself and all of its descendants
Here's an implementation:
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);
}
}
}
My problem is with the last class. You can see the ReSharper suggestion. And I am not feeling very good with down casting all the time. If I had to retrieve the owner at some other place, I would have to downcast to get the Stroke property again.
If I make SegmentVisual
generic SegmentVisual<T>
, with its owner being T
, it introduces a new obstacle because now the SegmentVisual
can only contain descendants of SegmentVisual<T>
which should not be the case because I want it to contain any type of SegmentVisual
, I just want the owner to be strongly typed.
I just want the SegmentVisual
to be able to correspond to a specific class so that its Owner
property is strongly typed. I can't seem to figure this out.
You can use new
to declare strongly typed Owner
in your subclass:
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 way base.Owner
will be casted each time you call this.Owner
, but at least you'll avoid repetitive code.
Second way is to use inheritance. Declare SegmentVisual
with basic functionality
abstract class SegmentVisual
{
public List<SegmentVisual> Descendants { get; private set; }
...
}
and OwnedSegmentVisual
with strongly typed owner
abstract class OwnedSegmentVisual<TOwner>: SegmentVisual where TOwner: Segment
{
public TOwner Owner { get; private set; }
protected OwnedSegmentVisual(TOwner owner)
{
Owner = owner;
}
}
Owner
can be used in subclasses without casting and common functionality can just use SegmentVisual
.
Third way is to use generics covariance, but you'll have to declare interfaces for your types:
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; }
}
Hope, this would be helpful.
C# does not support return type variance. That is, the following is not possible:
class Foo
{
virtual Base Owner{ get; }
}
class Bar: Foo
{
override Derived Owner { get; } // where Dervided: Base
}
You will get a compile time error informing you that Derived
does not override any suitable method. I find the lack of this feature in C# a bit annoying to be honest and although its been on the line a few times for future versions it has never made the cut. To be honest, competing features have been up to date quite a bit more interesting and useful.
IMO you have two reasonable options here. One is to hide Owner
:
class Bar: Foo
{
new Derived Owner { get { return base.Owner as Derived; } // where Derived: Base
}
The other is using explicitly implemented interfaces:
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
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.