Search code examples
c#genericsinterfacetype-constraints

Can I create a generic object within a generic class where the class of the object to be created has constraints on the generic type parameter?


I have a generic interface,

   public interface ICalculator<in T>
   {
      void Calculate(T input);
   }

a general calculator,

   public class GeneralCalculator<T> : ICalculator<T>
   {
      public void Calculate(T input)
      {
         bla bla
      }
   }

a specific calculator that works only on some types,

   public class SpecificAndFastCalculator<T> : ICalculator<T> where T : ISpecificInterface
   {
      public void Calculate(T input)
      {
         bla bla
      }
   }

and a decorator that decorates ICalculators:

   public class CalculatorDecorator<T> : ICalculator<T>
   {
      private readonly ICalculator<T> component;

      public CalculatorDecorator()
      {  
         component = (typeof (ISpecificInterface).IsAssignableFrom(typeof (T)))
                        ? new SpecificAndFastCalculator<T>() //Compiler Error 
                        : new GeneralCalculator<T>();
      }

      public void Calculate(T input)
      {
         component.Calculate(input); bla bla
      }
   }

The problem is, as you see, the compiler does not accept to call the constructor of SpecificAndFastCalculator because it doesn't know whether T obeys the constraint T : ISpecificInterface even though I check it at run-time. Can the compiler be right, or does it reject the code only because it is not smart enough?

Is there any way to assign the component field to a SpecificAndFastCalculator?

As far as I can see I have these options:

  • Write a specific decorator for having SpecificAndFastCalculator components, which I think beats the purpose of having decorators,
  • Use reflection magic somehow (I don't have any idea how)
  • Delete the T : ISpecificInterface constraint, document it, and hope for the best.

Is there any other way? For the time being I'm opting for the third option (deleting the constraint), what would you suggest?


Solution

  • It should work if you change the constructor of CalculatorDecorator<T> like this:

    public CalculatorDecorator()
    {  
        component = (ICalculator<T>)(typeof (ISpecificInterface).IsAssignableFrom(typeof (T))
            ? typeof(SpecificAndFastCalculator<>)
                .MakeGenericType(typeof(T))
                .GetConstructor(new Type[0])
                .Invoke(null)
            : new GeneralCalculator<T>());
    }
    

    That will make use of the specialized implementation and still make sure all elsewhere created instances will use it as well.