型がわからない非ジェネリッククラスからジェネリッククラスのメソッドを呼び出したい。
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();
}
}