I have an interface IPopUp
:
public interface IPopUp
{
bool IsCancelable {get;}
void Show();
void Close();
Action MainAction{ get; }
Action CancelAction{ get; }
}
I have several implementations for it: IfoPopUp
, ErrorPopUp
, and LoadingPopUp
I am creating the instances of the PopUps
with a PopUpManager
instance
public class PopUpManager
{
public void ShowPopUp<T>(string texto) where T:IPopUp
{
ShowPopup (()=>(IPopUp)Activator.CreateInstance (typeof(T)));
}
public void ShowPopUp<T>(string texto,Action mainAction)where T:IPopUp
{
ShowPopup (()=>(IPopUp)Activator.CreateInstance (typeof(T), mainAction));
}
public void ShowPopUp<T>(string texto,Action mainAction,Action cancelAction)where T:IPopUp
{
ShowPopup (()=>(IPopUp)Activator.CreateInstance (typeof(T), mainAction, cancelAction));
}
private void ShowPopup(Func<IPopUp> factoryFunc)
{
if (PopUpShowed != null) {
PopUpShowed.Close ();
}
IPopUp popUp = factoryFunc ();
popUp.Show ();
}
}
I don't know how can I force the IPopUp
implementations to implement the 3 types of constructor I am using in the PopUpManager
.
Abstract class won't work since it can force constructors...
Any ideas?
You have already found a nice solution to your question. Here some additional insights about forcing a class to implement certain constructors.
The C# language specification 5.0 defines in chapter 13 what an interface is:
An interface defines a contract. A class or struct that implements an interface must adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces.
Interfaces can contain methods, properties, events, and indexers. The interface itself does not provide implementations for the members that it defines. The interface merely specifies the members that must be supplied by classes or structs that implement the interface.
Instance constructors are not methods and can't be part of an interface. This is explicitly reminded in 13.2:
An interface cannot contain constants, fields, operators, instance constructors, destructors, or types, nor can an interface contain static members of any kind.
There are plenty of reasons that justify this language design choice, and you can find some additional explanations on SO. Note also that other languages have similar constraints. Java for example states that "Interfaces may not be directly instantiated."
The C# language specification says in section 10.1.1.1. that:
The abstract modifier is used to indicate that a class is incomplete and that it is intended to be used only as a base class. (...) An abstract class cannot be instantiated directly, and it is a compile-time error to use the new operator on an abstract class.
Long story short, if you would define three different constructors in an abstract class, the derived classes would have to invoke one of these constructors, but without giving any constraint on their own constructors. Note that this logic also applies to C++ or Java.
The link of the abstract class with its derived classes is like the link between a Shape
and the Square
, Rectangle
and Circle
that implement the Shape
: the construction paramters for a Rectangle can be very different from the construction parameters of a Circle, so that the Shape
can't put constraints on the constructor of its children.
You want the PopupManager
to instantiate an IPopUp
that has certain properties. Unfortunately, you can't instantiate directly an IPopUp
in a safe fashion (Activator.CreateInstance() might trigger an exception).
To make it safe, you could very well force IPopUp
to require a setter for MainAction
and CancelAction
, and add a constructor constraint (e.g. where T:IPopUp, new()
) to make sure that an instance can be created without surprise, and the object altered to fit the needs.
But in some case parameters need to be provided at construction. In this case you could consider using the builder pattern or the factory method as you've chosen. Note that in languages that allow interfaces with static members, you could even make three static factory methods mandatory in the IPopUp
interface.