0

I'm trying to build a list of generic items using the out generic modifier but I'm getting the error 'Invalid variance: The type parameter 'T' must be invariantly valid on 'ITestInterface.Value'. 'T' is covariant.'

I'm also getting an error trying to store value types as well as reference types.

I'd like a list of generic objects, each with different type T, and use type T in the interface.

Could someone tell me what I'm doing wrong?

public MainWindow()
{
   var list = new List<ITestInterface<object>>();
   list.Add(new Test<string>());
   list.Add(new Test<int>());

   foreach (var item in list)
   {
      var x = item.Value;
   }
}

public interface ITestInterface<out T>
{
   T Value { get; set; }
}

public class Test<T> : ITestInterface<T>
{
   public T Value { get; set; }
}

I've been working from https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-generic-modifier and Collection of derived classes that have generic base class.

Thanks for anyone's help!

Update:

Thank you to everyone who has helped me understand this area a bit more. I see now that I misunderstood the usage of the out/in generic modifier. The question and answers in Invalid variance: The type parameter 'T' must be contravariantly valid on 'UserQuery.IItem<T>.ItemList'. 'T' is covariant is definitely related to most of what I was trying to do, but I was also trying to add reference and value types as type parameters.

One (slightly dubious) work around that I found is to remove the generics from the class and use them instead in a static method that returns an instance of the class. Definitely not type safe, but it does allow reference and value types to be used:

public MainWindow()
{
    var list = new List<Test>();
    list.Add(Test.Create<string>("test"));
    list.Add(Test.Create<int>(5));

    foreach (var item in list)
    {
        var x = item.Value;
    }
}

public class Test
{
    public object Value { get; set; }
    public Type ValueType { get; set; }

    public static Test Create<T>(T value)
    {
        return new Test
        {
            Value = value,
            ValueType = typeof(T),
        };
    }

    public T GetValue<T>()
    {
        if (typeof(T) == ValueType)
            return (T)Convert.ChangeType(Value, typeof(T));
        else
            throw new InvalidOperationException();
    }
}
Simon Stanford
  • 353
  • 3
  • 10
  • 1
    `List` is neither covariant nor contravariant. If it's a list of `ITestInterface`s then it can never contain anything more specific. I suspect you're trying to achieve some kind of *runtime* type flexibility when you should realise that when you're *consuming* any generic type, the types are resolved at *compile* time. (Unless you're also exposing your functionality generically) – Damien_The_Unbeliever Mar 27 '18 at 17:24
  • 5
    "An interface that has a covariant type parameter enables its methods to return more derived types than those specified by the type parameter." But a property setter takes arguments, it does not return. By the same token, `in` will not allow a getter. If you have a property of type `T`, `T` cannot be variant. – Jeroen Mostert Mar 27 '18 at 17:24
  • Side note: check out https://stackoverflow.com/questions/12454794/why-covariance-and-contravariance-do-not-support-value-type for your `list.Add(new Test());` attempt. – Alexei Levenkov Mar 27 '18 at 18:19
  • Why the downvote? Due to the requirement for value types and reference types, this isn't an exact duplicate of another question. – Simon Stanford Mar 28 '18 at 14:25

1 Answers1

0

May be you can try like this? This is based on Jeroen Mostert's comment above and some R&D.

But this will not allow you to add value types. string will be fine as its a reference type but not int, or float etc.

public interface ITestInterface<out T>
{
     T GetValue();
}
public class Test<T> : ITestInterface<T>
{
    public T Value { get; set; }
    public T GetValue()
    {
        throw new NotImplementedException();
    }

    public void SetValue(T para)
    {
        Value = para;
    }
}
kay
  • 149
  • 1
  • 7
  • 2
    This seems more complicated than necessary. You can use `T Value { get; }` in the variant interface and implement this with `T Value { get; set; }` in the class, that's perfectly legal. Whether it's what you want is another matter. (Based on the OP's question, it's probably not.) – Jeroen Mostert Mar 27 '18 at 18:17
  • Very strange why you replaced property with getXxxx/setXxxx methods when simple `T Value { get; }` would work? – Alexei Levenkov Mar 27 '18 at 18:18
  • It was a quick recommended change by VS 2017. Not intentional. – kay Mar 27 '18 at 18:19
  • This is pretty close to what I was after! The part I didn't understand was the relationship between out/in and get/set. – Simon Stanford Mar 27 '18 at 19:42