繁体   English   中英

如何使用动态从非泛型类调用泛型类的方法?

[英]How to call method of generic class from non generic class using dynamic?

我想从不知道类型的非泛型类中调用泛型类的方法。

interface IShape
{
    SomeType AProperty;     // Edit 2, sorry this should be a property.
}

interface IShapeEditor
{
}

class ShapeEditorBase : IShapeEditor
{
}

class RectangleEditor<T> : ShapeEditorBase where T : IShape
{
    void Offset(T shape, int x, int y)
    {
    }
}

// This class must not be a generic class.
class OffsetRectangleButton
{
    void ButtonPressed(IShapeEditor editor)
    {
        IShape shape = ashape; // from somewhere
        dynamic editorInstance = editor;
        editorInstance.Offset(shape, 1, 1); // Will trigger an exception here!
    }
}

但是它触发了一个异常,它说C#在ShapeEditorBase中找不到Offset方法。
为什么不是RectangleEditor,而是ShapeEditorBase?
我该如何解决这个问题? 还是重构建议?

如果可能没有反射(我听说这不好)。

===================

编辑以获取其他信息:
-所有代码段都在一个控件框架中。
-应用程序通过通用类型
-OffsetRectangleButton或其他矩形按钮知道方法的所有知识,因为它们位于组件/ dll中。

====================

编辑3
-使代码与问题尽可能相似
-我重写代码以使先前的答案易于理解,而不是编辑先前的代码
-请参阅OffsetRectangleAction
-问题:接口IRectangleEditor是否正确? (而且我可以继续解决OffsetRectangleAction)
-问题:还是有更好的解决方案?

// =========================================
// Shape Display Component (a Winform Panel)
// =========================================

// In reality, rectangle is very complex implementation.
interface IRectangle // For rectangle display
{
    Rectangle Rectangle { get; set; }
}

interface ILine // ICircle etc for other displays
{
    Line Line { get; set; }
}

// Edit/Offset actions interface. It is represented by a button.
interface IAction
{
    void Run(IShapeDisplay display);
}

// Container of all display's actions.
interface IActionContainer
{
    IList<IAction> Actions; // Every action is related to a control (button, edit box, etc).
}   

// Please see "OffsetRectangleAction".
// This class is container of all RectangleDisplays actions (as buttons in UI).
// This is also important, because this class shows why OffsetRectangleAction must not be generic.
// If OffsetRectangleAction is generic, then this class must be generic because it has OffsetRectangleAction.
// If this class generic, then in user interface has more than one OffsetRectangleAction buttons are created.
// And each OffsetRectangleAction edit certain type of Rectangle generic.
class RectangleActionContainer : IActionContainer
{
    // List of all actions/buttons
    IList<IAction> Actions;    

    RectangleActionContainer()
    {
        // It must be able to edit various types of rectangle.
        this.Actions.Add(new EditAction());
        this.Actions.Add(new OffsetRectangleAction());
    }
}

// This class must not be a generic class. Please see RectangleActionContainer class.
// This is the most important problem!!!!!!!!!!!!!!!!!!!!!!!!!
// How can I access RectangleDisplay.RetctangleEditor.Offset or Edit
class OffsetRectangleAction: IAction // Similar also EditRectangleAction, InflateRectangleAction
{
    void Run(IShapeDisplay display)
    {
        IRectangle rectangle = this.GelectedRectangle(); // from somewhere

        // How to make it runs properly without exception.
        dynamic rectangleDisplayInstance = display;
        dynamic editHandler = rectangleDisplayInstance.EditHandler;
        editHandler.Offset(rectangle, 1, 1); // Will trigger an exception here!
    }
}

// RectangleEditor interface to edit rectangles and as rectangles container.
interface IRectangleEditor<T> where T : IRectangle // Generic because there are many type rectangle for various purposes. 
{
    IList<T> Rectangles { get; } // List of rectangle to be edited/visualized.

    Edit(T rect, Rectangle dimension);  

    Offset(T rect, double x, double y);
}

// Shape display interface
interface IShapeDisplay
{
    IActionContainer ActionContainer { get; }

    void Draw(Graphics canvas);
}

// Base of all displays (rectangle, line, circle)
class ShapeDisplayBase : IShapeDisplay
{
    IActionContainer ActionContainer { get; private set; }

    virtual void Draw(Graphics canvas)
    {
    }
}

// Rectangle Display.
// Controller for displaying and bussines logic of rectangle shapes.
// ShapeDisplayBase inherited also for Line, Circular etc displays.
class RectangleDisplay<T> : ShapeDisplayBase where T : IShape
{
    IRectangleEditor<T> RectangleEditor { get; private set; }

    RectangleDisplay<T>(IRectangleEditor<T> editor)
    {
        this.RectangleEditor = editor;
    }

    override void Draw(Graphics canvas)
    {
    }
}

// Controller for the whole shape displays (Rectangle, Line, Circle etc.)
class ShapeDisplaysController
{
    IList<IActionContainer> ActionContainers { get; private set; } // Action container for all displays (rectangle, line, etc)

    IList<IShapeDisplay> ShapeDisplays { get; private set; } // Can be RectangleDisplay, LineDisplay, CircularDisplay

    void RegisterDisplay(IShapeDisplay display)
    {
       this.ShapeDisplays = display;

       // A type of displays can only one action container (eg: many rectangle types can be edited only with one edit button)
       if (!ActionContainer.Contains(display))
       {
           ActionContainers.Add(display.ActionContainer);
       }
    }

    // Show all shape display objects.
    void Draw(Graphics canvas)
    {
        foreach(var display in this.ShapeDisplays)
        {
            display.Draw(canvas);
        }
    }
}

// ==========================
// Application 
// ==========================

class RoundedRectangleEditHandler<T> : IRectangleEditor<T> where T : IRectangle
{
     IList<T> Rectangles { get; private set;} // List of rectangle to be edited/visualized.
     implementation...
}

class FillRectangleEditHandler<T> : IRectangleEditor<T> where T : IRectangle
{
     IList<T> Rectangles { get; private set;} // List of rectangle to be edited/visualized.
     implementation...
}

// There are many application that use ShapeViewer component.
// And every application has their own rectangle type, so we decided to use generic.
class ShapeViewerApp: Form
{
    // Visualization control for shape.
    private ShapeDisplaysController myShapeDisplaysController;

    // Show shape list in grid.
    private GridController myGridController;

    static void Main()
    {
        this.myShapeDisplaysController = new ShapeDisplaysController();

         var roundedRectangleEditHandler = new RoundedRectangleEditHandler<RoundedRect>();
         var roundedRectangleDisplay = new RoundedRectangleDisplay<RoundedRect>(roundedRectangleEditHandler);

         var fillRectangleEditHandler = new FillRectangleEditHandler<FillRectangle>();
         var fillRectangleDisplay = new FillRectangleDisplay<FillRectangle>(fillRectangleEditHandler);           

         this.myShapeDisplaysController.Register(fillRectangleDisplay);
         this.myShapeDisplaysController.Register(roundedRectangleDisplay);
         this.myShapeDisplaysController.Register(lineDisplay);
         this.myShapeDisplaysController.Register(circleDisplay);

         // ShapeDisplaysController is a Winform Panel UI.
         this.Controls.Add(this.myShapeDisplaysController.Control);
         this.ShowDialog();
    }
}

IShapeEditor不包含您的Offset方法,因此程序不知道该方法存在于该对象上。 如果可以确定它是这种类型,并且可以避免使用dynamic关键字,则可以将其强制转换为该类型。

我建议您将方法添加到IShapeEditor ,或者以其他方式使用您关心的确切类型,因为您实现的类引用的是Rectangle ,因此仅考虑Rectangle而不关心任何其他IShape才有意义。

如何将Offset移动到IShapeEditor并在ShapeEditorBase实现呢? 然后,您无需使用dynamic

class ShapeEditorBase : IShapeEditor
{
    public void Offset(IShape shape, int x, int y)
    {
    }
}

class RectangleEditor<T> : ShapeEditorBase where T : IShape
{
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM