Reputation: 153
I found something strange for me in type inference in C#.
The example.
I've got an interface
interface IInterface1
{
}
and a class that implements the interface
class Class1 : IInterface1
{
}
Then I have a function that creates the class
static Class1 GetInstanceOfClass1()
{
return new Class1();
}
And I want to use generic function that will return an enumerable
static IEnumerable<T> GetSomething<T>() where T : IInterface1
{
yield return GetInstanceOfClass1();
}
The full code is
using System.Collections.Generic;
namespace TypeInference
{
interface IInterface1
{
}
class Class1 : IInterface1
{
}
class Program
{
static Class1 GetInstanceOfClass1()
{
return new Class1();
}
static IEnumerable<T> GetSomething<T>() where T : IInterface1
{
yield return GetInstanceOfClass1();
}
static void Main(string[] args)
{
}
}
}
This code is not compiled
Cannot implicitly convert type 'TypeInference.Class1' to 'T'
If i write as
yield return (T)GetInstanceOfClass1();
the error message is as
Cannot convert type 'TypeInference.Class1' to 'T'
it cannot convert as before.
Ok. I write as
yield return (IInterface1)GetInstanceOfClass1();
and got
Cannot implicitly convert type 'TypeInference.IInterface1' to 'T'
it cannot convert as before.
But if I write as
yield return (T)(IInterface1)GetInstanceOfClass1();
everything is ok.
Can someone explain me what was wrong and why the code eventually is compiled?
Thank you.
Upvotes: 4
Views: 92
Reputation: 93090
Your type parameter T is guaranteed to implement IInterface1
, but its relation with Class1
is unspecified.
Thus:
Class1
and also Class1
might not be a super class of T => (T)GetInstanceOfClass1()
fails IInterface
from a method that returns T => (IInterface1)GetInstanceOfClass1()
fails The one that works:
You can of course cast Class1
to IInterface
because Class1
implements the interface.
Now since T also implements IInterface it could be a valid cast or it could fail at runtime.
This is similar to how you can convert any object to a given type at compile time, but if the runtime type of the object is wrong it would fail at run time.
E.g.
object o;
o = 5;
string s = (string)o;
This compiles, but fails at run time.
Upvotes: 3
Reputation:
It's to prevent yourself from shooting yourself in the foot. Like you managed to end up doing anyway.
What happens if you define
interface IInterface2 : IInterface1 { }
and then call GetSomething<IInterface2>()
? It's a valid generic type argument, but Class1
doesn't implement IInterface2
.
Worse yet, what happens if you define
class Class2 : IInterface1 { }
and then call GetSomething<Class2>()
?
Your design is broken, you need to think about it some more, and not continue working around the compiler errors until you have a design that actually has a chance of working.
Upvotes: 8