【问题标题】:In Asp.net, how would you render a subclass to an html helper?在 Asp.net 中,如何将子类呈现给 html 助手?
【发布时间】:2011-01-19 21:02:31
【问题描述】:

如何使用 TriangleExtension 和 SquareExtension 辅助方法呈现我的 Document 类?

这是我的示例设置:

public class Document
{
    public List<Shape> Shapes = new List<Shape>();
    public Document()
    {
        Shapes.Add(new Triangle());
        Shapes.Add(new Square());
    }   
}

public abstract class Shape
{
    int width = 5;
}

public class Triangle : Shape
{
    int height = 10;
}

public class Square : Shape
{
    int height = 10;
}

我将一个 Document 对象传递给我的视图,并希望将其呈现如下:

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)
}

编辑:这个问题是另一种问我真正想问什么的方式:ASP.net MVC - rendering a List containing different types, with a different view for each type

【问题讨论】:

    标签: c# asp.net oop


    【解决方案1】:

    很难准确地说出您要做什么,但是您的示例代码会违反 Liskov 替换原则。您的渲染方法应该适用于传入的每个形状。因此,如果稍后添加平行四边形,则无需更改页面逻辑。它应该可以工作,因为它是一个形状。

    下面的代码应该可以帮到你。

    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
        }
     }
    

    那么您的渲染外观可能如下所示:

      var renderer = new HtmlRenderer();
      foreach (var shape in shapes)
      {
        shape.Render(renderer);
      }
    

    现在,您创建的每个形状只需要知道如何使用渲染器提供的指令来创建自己。您可以渲染 Html 或图像或 ascii 艺术,只要您为其创建渲染器即可。

    另一种选择是做类似于GetEnumerator() 在集合上的工作方式,并包含一个知道如何进行绘图的内部私有类。然后您可以向GetDrawInstructions() 询问形状并使用渲染器。这样,您的形状和绘图说明在不同的类中是不同的关注点。只是其中一个嵌套在另一个中。

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

    最后一个选择是使用 DrawInstructionsRegistry,并在注册表中注册每个形状的绘图指令,然后在需要时调用它。

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

    这些其他选项可能会解决您的问题,但我认为这可能比它的价值更麻烦。

    【讨论】:

    • +1 - 类似于 GDI(+) 和 WPF 如何将对象呈现到屏幕和打印机设备。将介质抽象出来,提供绘制图元的函数,理论上可以用来表示任何形状。
    • 是的——但看起来数据模型和构建视图的逻辑是耦合的,违反了单一责任原则。也许没有别的办法。
    • 形状本身就是可见的。我认为形状知道如何使用一些简单的命令来绘制自己是有道理的。它不知道它是在 HTML 页面上还是在图像文件中绘制的。你的例子可能是人为的(我不确定三角形有多少改变的原因,但据我所知它还没有发生。但你永远不能太小心),但如果你愿意我可以添加另一层或两层抽象。示例即将出现...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-08
    • 1970-01-01
    • 2011-09-09
    • 2022-12-18
    • 1970-01-01
    • 2010-09-09
    • 2016-06-30
    相关资源
    最近更新 更多