In Asp.net, how would you render a subclass to an html helper?
How would I render my Document class, using the TriangleExtension and SquareExtension helper methods?
Here's my sample setup:
public class Document
{
public List<Shape> Shapes = new List<Shape>();
public Document()
{
Shapes.Add(new Triangle());
Shapes.Add(new Squa开发者_C百科re());
}
}
public abstract class Shape
{
int width = 5;
}
public class Triangle : Shape
{
int height = 10;
}
public class Square : Shape
{
int height = 10;
}
I pass a Document object to my view, and would like to render it something like this:
foreach (var shape in Model.Shapes)
{
//what code do I put here?
//e.g. if (shape.GetType=="Triangle")
// Helper.Triangle(shape)
// if (shape.getTYpe=="Square")
// Helper.Square(shape)
}
EDIT: This question is another way to ask what I was really trying to ask: ASP.net MVC - rendering a List containing different types, with a different view for each type
It's hard to tell exactly what you are trying to do, but your sample code would violate the Liskov substitution principal. Your render method should work with each and any shape that gets passed in. So, if later you add Parallelogram, you don't need to change your page logic. It should just work because it is a shape.
Something like the code below should help you.
public interface IShapeRenderer
{
void DrawLine(int x1, int y1, int x2, int y2);
}
public abstract class Shape
{
public abstract void Render(IShapeRenderer renderer);
}
public class Triangle: Shape
{
public override void Render(IShapeRenderer renderer)
{
// draw 3 lines
}
}
public class Square: Shape
{
public override void Render(IShapeRenderer renderer)
{
// draw 4 lines
}
}
public class HtmlRenderer: IShapeRenderer
{
public void DrawLine(int x1, int y1, int x2, int y2)
{
// draw html line
}
}
Then your render look could look like this:
var renderer = new HtmlRenderer();
foreach (var shape in shapes)
{
shape.Render(renderer);
}
Now each shape that you create just needs to know how to use the instructions provided by the renderer to create itself. You could render Html or an image or ascii art, just as long as you create a renderer for it.
Another option would be to do something similar to how GetEnumerator()
works on collections, and include an inner private class that knows how to do the drawing. Then you can ask the shape to GetDrawInstructions()
and use the renderer with that. This way, your shape and the drawing instructions are separate concerns in separate classes. Just that one of them in nested in the other.
public interface IDrawInstructions
{
void Draw(IShapeRenderer renderer);
}
public abstract class Shape
{
public abstract IDrawInstructions GetDrawInstructions();
}
public class Triangle: Shape
{
public override IDrawInstructions GetDrawInstructions()
{
return new TriangleDrawInstructions(this);
}
private class TriangleDrawInstructions: IDrawInstructions
{
public Triangle Triangle { get; private set; }
public TriangleDrawInstructions(Triangle triangle)
{
Triangle = triangle;
}
public void Draw(IShapeRenderer renderer)
{
// draw 3 lines using information from triangle
}
}
}
var renderer = new HtmlRenderer();
foreach (var shape in shapes)
{
var instructions = shape.GetDrawInstructions();
instructions.Draw(renderer);
}
A final option would be to use a DrawInstructionsRegistry, and register the drawing instructions for each shape in the registry and then recall it when you need it.
public class ShapeDrawInstructionsRegistry
{
private static Dictionary<Type, IDrawInstructions> _registry = new Dictionary<Type, IDrawInstructions>();
public static void Register<T>(IDrawInstructions instructions) where T: Shape
{
var type = typeof (T);
if(_registry.ContainsKey(type))
_registry[type] = instructions;
else
{
_registry.Add(type, instructions);
}
}
public static IDrawInstructions Lookup(Shape shape)
{
var type = shape.GetType();
if (!_registry.ContainsKey(type)) return null;
return _registry[type];
}
}
public class SquareDrawInstructions: IDrawInstructions
{
public void Draw(Shape shape, IShapeRenderer renderer)
{
var square = shape as Square;
if (square == null) throw new Exception();
// draw 4 sides
}
}
// in your global.asax.cs or bootstrapper class
ShapeDrawInstructionsRegistry.Register<Square>(new SquareDrawInstructions());
ShapeDrawInstructionsRegistry.Register<Triangle>(new TriangleDrawInstructions());
// your loop
var renderer = new HtmlRenderer();
foreach (var shape in shapes)
{
var instructions = ShapeDrawInstructionsRegistry.Lookup(shape);
instructions.Draw(shape, renderer);
}
Those other options might address your concern, but I think it's probably more trouble than it is worth.
精彩评论