UPDATE: This isn't about getting it to compile. The question is, why does the C# compiler allow the cast when using an interface, but it can't figure out the type when I use a class that implements the same interface.
I am getting the following error:
Cannot convert type 'Amber.BLL.iWeb.Session.AppSession' to 'TService'
Here is the code:
public override TService GetService<TService>()
{
if ( typeof( TService ) == typeof( IAppSession ) )
{
AppSession session = new AppSession();
return (TService) session;
}
throw new Exception( String.Format(
"iWebFactoryProvider cannot create services of type '{0}'.",
typeof( TService ).Name ) );
}
As it so happens, the AppSession
class implements the IAppSession
interface. If I change the line of code that instantiates AppSession
to use the interface, like this:
IAppSession session = new AppSession();
suddenly everything compiles fine. I also note that it compiles fine if I do this:
AppSession session = new AppSession();
return (TService) (IAppSession) session;
In case it matters, the GetService() is overriding a method whose signature is declared like this:
public virtual TService GetService<TService>() where TService : class
In short, I can't figure out what the rules should be here so I can know how to avoid this situation in the future. Why was the compiler happy to cast the interface, but not happy to cast the interface's implementing class?
I note that this question is asking about a similar issue, but the answer isn't detailed enough for me to understand how it applies to my situation.
Unlike C++ and Java, C doesn't support generics. How to create a linked list in C that can be used for any data type? In C, we can use a void pointer and a function pointer to implement the same functionality. The great thing about void pointer is it can be used to point to any data type.
In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs.
Generics are statements such as “tigers are striped”, “a duck lays eggs”, “the dodo is extinct”, and “ticks carry Lyme disease”. Generics express generalizations, but unlike quantified statements, generics do not carry information about how many members of the kind or category have the property.
Generics are generic until the types are substituted for them at runtime. Templates are specialized at compile time so they are not still parameterized types at runtime. The common language runtime specifically supports generics in MSIL.
Why does the C# compiler allow the cast when using an interface, but it can't figure out the type when I use a class that implements the same interface?
Good question. Consider the following:
public interface I {}
public class D {} // Note that D does not even implement I!
public class E
{
public static M<T>(T t)
{
D d1 = (D)t; // Illegal
D d2 = (D)(object)t; // Legal
D d3 = (D)(I)t; // Legal
}
}
Let's break your question up into three questions.
Why is the cast directly from
T
toD
illegal?
Suppose it were legal. Then E.M<D>(new D())
would work just fine; we'd cast the T
to D
and in fact it is a D
, so no problem.
Now suppose we create an entirely different assembly with:
class C
{
public static explicit operator D(C c) { whatever }
}
And you call E.M<C>(new C())
in that assembly.. What do you reasonably expect to happen? You have an object of type C
, it is being cast to D
, and there is an explicit conversion operator right there from C
to D
. Most people would reasonably expect that the explicit conversion operator would be called.
But how on earth is the compiler supposed to realize when compiling the body of M
that someone in the future might create a class C
in a completely different assembly? The compiler has no way to emit the call to the conversion operator when compiling M
. So we have three choices:
In short, our choices are (1) make generics inconsistent, (2) make generics slow and unpredictable, or (3) disallow a feature that is already working against genericity. This is an easy choice to make; we chose (3).
If you want (2), you can have it in C# 4; dynamic
starts the compiler again at runtime and works out whether there is an explicit conversion operator.
Why is the cast indirectly from
T
toD
via object legal?
Because now no user-defined conversion can be relevant; there is never a user-defined conversion from object to anything.
Why is the cast indirectly from
T
toD
viaI
legal?
Because now no user-defined conversion can be relevant; there is never a user-defined conversion from an interface to anything.
Bonus question:
But
D
does not even implementI
! What's up with that?
A derived class of D
might:
class F : D, I {}
...
E.M<D>(new F());
Now t
can be cast to I
because it might implement I
, and I
can be cast to D
because it might be F
.
If D
were sealed
then it would not be legal to cast from I
to D
because then there could not possibly be a derived F
type.
Have you tried adding a constraint for IAppSession
?
public virtual TService GetService<TService>() where TService : IAppSession, class
That linked question is exactly the same issue. The compiler doesn't know that TService
can be an AppSession
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With