0

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?

makambi
  • 1,100
  • 3
  • 13
  • 30

2 Answers2

3

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.

Kris Vandermotten
  • 10,111
  • 38
  • 49
  • But technically `DataAlgorithm ` is IAlgorithm, because DataAlgorithm is IAlgorithm and IDataParams are IParams. I mean, I would expect it to work in this way. Thanks for sample, I will try. – makambi Jun 16 '14 at 11:32
  • @makambi No it is not. That is what I try to explain in the first half of my answer. – Kris Vandermotten Jun 16 '14 at 11:33
1

To expand on Kris's answer above, check out the article on covariance and contravariance on MSDN.

To make it simple, rather than an IAlgorithm<IDataParams>, imagine a List<IParams>. Any type implementing IParams should be able to be stored in that list. However, imagine the following code:

    List<IParams> lst = new List<IDataParams>();
    lst.Add(new ClassImplementingIParamButNotIDataParams());

As List<IDataParams> only accepts IDataParams, the latter statement would be invalid. However, List<IDataParams> lst = new List<IParams>(); is valid: all IDataParams that could be added to the list will also be IParams.

This is just an example; as Kris said, you can control the covariance/contravariance of a type parameter with the in and out keywords on your generic. The MSDN article is a good start.

Lawrence Craft
  • 314
  • 1
  • 4
  • `List lst = new List();` is not valid! `List` is not contravariant. Only interfaces can be covariant or contravariant, classes cannot. The reason that this is invalid is simple: what is the type of `lst.First()` ? Since lst is a variable of type `List`, it would have to be something that implements `IDataParams`. But if a `new List()` is assigned to `lst`, that cannot be guaranteed. Just to be very complete: a `List` can be assigned to a variable of type `List` if and only if T == U. – Kris Vandermotten Jun 16 '14 at 22:53