4

I have a problem with casting my implementing classes to my generic interface. I have several internal classes inside a class that implement this interface. Then I have a method that is supposed to return any one of these classes, but the cast it contains fails for T != String.

Sample:

public interface IMyInterface<out T> where T : IConvertible
{/*Contents*/}

internal class MyStringClass : IMyInterface<String>  {/*Contents*/}
internal class MySingleClass : IMyInterface<Single>  {/*Contents*/}

IMyInterface<IConvertible> CreateMyObject(Type type)
{
    return (IMyInterface<IConvertible>)Activator.CreateInstance(type);
}

Seeing as String is a reference type, while the others are structs, is this related? There must be something I'm missing here. (Possibly related as well: If I remove the covariance of T, casting MyStringClass fails as well.)

I don't usually work with C# so this is a bit unfamiliar to me. Any explanation of why this doesn't work, as well as any pointer towards a solution is welcome.

EDIT: My internal classes that implement IMyInterface are passed as type (typeof)

EDIT2: As Alireza explained in an answer further down, making T covariant makes it not support value, types, which explains why T = String works, but not T = ValueType. I'm still stuck as to how I can make this work though...

user3839796
  • 51
  • 1
  • 6

1 Answers1

5

That out modifier you put in front of T made it covariant, but from MSDN document for out:

Covariance and contravariance are supported for reference types, but they are not supported for value types.

UPDATE: Sometimes (if your design allows it and you don't need the non-generic stuff outside the class) you can use a non-generic interface to solve the problem:

public interface IMyInterface
{ /*non-generic content*/}

public interface IMyInterface<out T> : IMyInterface where T : IConvertible
{/*Contents*/}

internal class MyStringClass : IMyInterface<String> {/*Contents*/}
internal class MySingleClass : IMyInterface<Single> {/*Contents*/}

static IMyInterface CreateMyObject(Type type)
{
    return (IMyInterface)Activator.CreateInstance(type);
}
svick
  • 236,525
  • 50
  • 385
  • 514
Alireza
  • 4,976
  • 1
  • 23
  • 36
  • Thanks. I didn't realize that. However, if I remove the covariance, casting `MyStringClass` to the interface doesn't work anymore either, and that still leaves the question of why the cast doesn't work in the first place. – user3839796 Jul 15 '14 at 10:30
  • It is because unlike `array`s generic types of compatible types are not compatible themselves. I mean a `string[]` can be assigned to an `IConvertible[]` but `IMyInterface` and `IMyInterface` are totally different types. – Alireza Jul 15 '14 at 10:39
  • the `out` modifier just makes that assignment of course with some limitations possible for reference types. – Alireza Jul 15 '14 at 10:43
  • 1
    I see. I guess this answers my question of why it doesn't work. Going to mark answer as accepted because it answered the question, but if you (or anyone else) have a solution for how to make it work, it would be very much appreciated. – user3839796 Jul 15 '14 at 10:48
  • 3
    @user3839796 A `String` (reference type) is an `IConvertible` through a ***reference conversion***. Therefore the covariance of `IMyInterface` applies, and an `IMyInterface` is an `IMyInterface`. On the other hand, a `Single` (value type, a.k.a. `float`) is an `IConvertible` through a ***boxing conversion***, and as stated in the answer here, covariance does not apply in that case. So yes, it is because it is a value type (`struct`). – Jeppe Stig Nielsen Jul 15 '14 at 10:54
  • 1
    According to your design, sometimes you can define a non-generic interface `IMyInterface` as a parent for `IMyInterface` then put anything non-generic inside this interface and specify that type as your return value – Alireza Jul 15 '14 at 10:56
  • I want to access a method with a generic return parameter, but what you suggest might possibly work with some workarounds. It's at least way closer to a solution than I was previously. – user3839796 Jul 15 '14 at 11:18