Search code examples
c#genericscovariancecontravariance

Child to parent generic convertion in c#


I have my base interface class:

 public interface IAlgorithm<T> where T : IParams
 {
        /// <summary>
        /// Contains algorythm parameters
        /// </summary>
        T Params{ get; set; }
 }

And

public interface IParams
{
}

Then I have my basic class for algorithm implementation:

public abstract class BaseAlgorithm<T> : IAlgorithm<T> where T: IParams
{
    public virtual T Params { get; set; }
}

 public class DataAlgorithm : BaseAlgorithm<IDataParams>
 {
        public override IDataParams Params { get; set; }
 }

And params:

public interface IDataParams : IParams
{
}

There are multiple of BaseAlgorithm class implementations.

Now I want to create new isntance of DataAlgorith and assign it to the parent interface:

IAlgorithm<IParams> algorithm = new DataAlgorithm();

but it wont work and produces error message:

Cannot implicitly convert type 'DataAlgorithm' to 'IAlgorithm<IParams>'. An explicit conversion exists (are you missing a cast?)

The code below will work just fine:

IAlgorithm<IDataParams> algorithm = new DataAlgorithm();

Any ideas how to assign to parent type?


Solution

  • You cannot do this, because a DataAlgorithm is an IAlgorithm<IDataParams>, but not an IAlgorithm<IParams>.

    Think about it. If it was, you would be able to do

    IAlgorithm<IParams> algorithm = new DataAlgorithm();
    
    algorithm.Params = ... some other kind of IParams but not IDataParams.
    

    Obviously, that would make no sense.

    If an object is an IAlgorithm<IParams>, that means you can assign any object that implements IParams to its Params property. That is not the case for an object that implements IAlgorithm<IDataParams> as that only accepts implementations of IDataParams in the Params setter. So an IAlgorithm<IDataParams> is not an IAlgorithm<IParams>.

    However, if the setter on the Params property is not required, you can make the interface covariant.

    public interface IAlgorithm<out T> where T : IParams
    {
        /// <summary>
        /// Contains algorythm parameters
        /// </summary>
        T Params{ get; }
    }
    

    Adjust your base class accordingly.