-1

i have this code:

public interface IDataScopeType { }
public interface IDataProcessor<in T> where T : IDataScopeType { }

public class DataScopeType : IDataScopeType { }
public class DataProcessor : IDataProcessor<DataScopeType> { }

public class Executor
{
    List<IDataProcessor<IDataScopeType>> processors;
    
    public Executor()
    {
        processors = new List<IDataProcessor<IDataScopeType>>
        {
            new DataProcessor(),
        };
    }
}

Please explain why the compiler shows an error "Cannot convert from DataProcessor to IDataProcessor" when I try to add a new item to the list? Or am I doing something wrong?

  • 4
    What you want to make this work is co-variance, but the generic type is defined as contra-variant with the `in` operator. If you change it to `out` it will work, but note that means that the generic type can only be the return type of methods and not the arguments and it can only be used for read-only properties. – juharr Aug 10 '21 at 13:36
  • 1
    Does this answer your question? [C# variance problem: Assigning List as List](https://stackoverflow.com/questions/2033912/) and [still confused about covariance and contravariance & in/out](https://stackoverflow.com/questions/3445631/) and [C# generic inheritance and covariance part 2](https://stackoverflow.com/questions/14263964/) and [How to create List of open generic type](https://stackoverflow.com/questions/58570948/) –  Aug 10 '21 at 13:37
  • @OlivierRogier That's not quite the same thing. The fact that the OP has a `List` is not important. It's that they want to assign a `DataProcessor` to `IDataProcessor`. – juharr Aug 10 '21 at 13:39
  • @juharr , is there a way to make generic not as return type but also as argument of the methods? – Denis Malakhov Aug 10 '21 at 13:40
  • 1
    @juharr The underlying explanation proceeds from the same problem regarding the true generic polymorphism on open types using the diamond operator which does not yet exist in C# and requires the use of non-generic interfaces to be solved. –  Aug 10 '21 at 13:41
  • 1
    @DenisMalakhov That sounds like it's contra-variant but it means you cannot do the type of casting that you are trying to do. Basically co-variant allows you to cast a more derived type to a less derived type because it comes "out" of the interface, but contra-variant allows you to cast a less derived type to a more derived type because it goes "in" the interface. – juharr Aug 10 '21 at 13:42
  • 1
    @juharr It's a pity that my idea can't be implemented yet. Thank you for the clarification! – Denis Malakhov Aug 10 '21 at 13:50
  • 1
    @DenisMalakhov Indeed the .NET/C # generics are only in the prehistoric age and I can't wait for the diamond operator to be available for these things (in addition to multiple class inheritance but that's another topic). –  Aug 10 '21 at 13:54
  • @DenisMalakhov It's not a pity it's so that you don't cast something in such a way that it can result in invalid code. If it allowed your cast then you'd have a reference that could pass anything that implements `IDataScopeType` into your `DataProcessor` even though it really can only take `DataScopeType` – juharr Aug 10 '21 at 15:40

1 Answers1

0

The List<> item type is IDataProcessor<IDataScopeType>, the class DataProcessor inherits from IDataProcessor<DataScopeType> which is a different type despite that DataScopeType inherits from IDataScopeType, because, for example, when you have implemented DataScopeType you could add a data members and/or methods of which IDataProcessor<DataScopeType> will be aware of, so it has access to DataScopeType, and not to the less concrete interface IDataScopeType. To fix the issue there is a couple of ways you can do that:

  1. Change the DataProcessor generic type parameter to be the interface type, like that: public class DataProcessor : IDataProcessor<IDataScopeType> { }

  2. OR, Declare the list with the type that match the base class type of DataProcessor, like that: List<IDataProcessor<DataScopeType>>.