Search code examples
c#interfaceclass-designabstract-class

How can I force a class to implements constructor?


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?


Solution

  • You have already found a nice solution to your question. Here some additional insights about forcing a class to implement certain constructors.

    Can it be done with an interface ?

    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."

    Can it be done with an abstract class ?

    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.

    Can it be done with a combination ?

    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.