Одним из способов решения этой проблемы является использование обобщений и ограничения new ().
Вместо того, чтобы выражать свой конструктор как метод / функцию, вы можете выразить его как фабричный класс / интерфейс. Если вы укажете общее ограничение new () на каждом сайте вызовов, для которого необходимо создать объект вашего класса, вы сможете соответствующим образом передавать аргументы конструктора.
Для вашего примера IDrawable:
public interface IDrawable
{
void Update();
void Draw();
}
public interface IDrawableConstructor<T> where T : IDrawable
{
T Construct(GraphicsDeviceManager manager);
}
public class Triangle : IDrawable
{
public GraphicsDeviceManager Manager { get; set; }
public void Draw() { ... }
public void Update() { ... }
public Triangle(GraphicsDeviceManager manager)
{
Manager = manager;
}
}
public TriangleConstructor : IDrawableConstructor<Triangle>
{
public Triangle Construct(GraphicsDeviceManager manager)
{
return new Triangle(manager);
}
}
Теперь, когда вы используете его:
public void SomeMethod<TBuilder>(GraphicsDeviceManager manager)
where TBuilder: IDrawableConstructor<Triangle>, new()
{
// If we need to create a triangle
Triangle triangle = new TBuilder().Construct(manager);
// Do whatever with triangle
}
Вы даже можете сконцентрировать все методы создания в одном классе, используя явную реализацию интерфейса:
public DrawableConstructor : IDrawableConstructor<Triangle>,
IDrawableConstructor<Square>,
IDrawableConstructor<Circle>
{
Triangle IDrawableConstructor<Triangle>.Construct(GraphicsDeviceManager manager)
{
return new Triangle(manager);
}
Square IDrawableConstructor<Square>.Construct(GraphicsDeviceManager manager)
{
return new Square(manager);
}
Circle IDrawableConstructor<Circle>.Construct(GraphicsDeviceManager manager)
{
return new Circle(manager);
}
}
Чтобы использовать это:
public void SomeMethod<TBuilder, TShape>(GraphicsDeviceManager manager)
where TBuilder: IDrawableConstructor<TShape>, new()
{
// If we need to create an arbitrary shape
TShape shape = new TBuilder().Construct(manager);
// Do whatever with the shape
}
Другой способ - использовать лямбда-выражения в качестве инициализаторов. В какой-то момент на ранней стадии иерархии вызовов вы будете знать, какие объекты вам нужно создать (т.е. когда вы создаете или получаете ссылку на свой объект GraphicsDeviceManager). Как только у вас это получится, пройдите лямбду
() => new Triangle(manager)
к последующим методам, чтобы они знали, как с этого момента создавать Треугольник. Если вы не можете определить все возможные методы, которые вам понадобятся, вы всегда можете создать словарь типов, которые реализуют IDrawable, используя отражение, и зарегистрировать лямбда-выражение, показанное выше, в словаре, который вы можете сохранить в общем месте или передать в дальнейшие вызовы функций.