Generics in C # - Cannot convert 'classname' to 'TGenericClass'

UPDATE: This is not about compilation. The question is why the C # compiler allows use when using an interface, but it cannot determine the type when I use a class that implements the same interface.

I get 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 happens, the AppSession class implements the IAppSession interface. If I changed the line of code that creates an instance of AppSession to use the interface, for example:

 IAppSession session = new AppSession(); 

all of a sudden everything compiles fine. I also note that it compiles fine if I do this:

 AppSession session = new AppSession(); return (TService) (IAppSession) session; 

In this case, GetService () overrides the method whose signature is declared as follows:

 public virtual TService GetService<TService>() where TService : class 

In short, I cannot understand what rules should be here so that I can know how to avoid this situation in the future. Why was the compiler happy to apply the interface, but not happy to use the interface implementation class?

I note that this question asks about a similar problem, but the answer is not detailed enough for me to understand how this relates to my situation.

+4
source share
3 answers

Why does the C # compiler allow use when using an interface, but it cannot determine 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 me break your question into three questions.

Why is casting directly from T to D forbidden?

Suppose this was legal. Then EM<D>(new D()) will work fine; we would put T on D and it’s actually D , so no problem.

Now suppose we create a completely different assembly with:

 class C { public static explicit operator D(C c) { whatever } } 

And you call EM<C>(new C()) in this assembly. What do you reasonably expect? You have an object of type C , it is added to D , and there is an explicit conversion operator from C to D Most people reasonably expected the explicit conversion operator to be called.

But how should the compiler really realize when compiling the body of M that someone in the future can create a class C in a completely different assembly? The compiler cannot fix the call to the conversion operator when compiling M So, we have three options:

  • Make cast statements sometimes use explicit conversion operators, and sometimes not, depending on whether you are in general or not.
  • Make literal statements run the compiler again at run time to look for explicit conversion statements that could be added to different assemblies after the source code has been compiled.
  • Disallow listing first.

In short, our choices are: (1) make generics inconsistent, (2) make generics slow and unpredictable, or (3) inhibit a function that already works against typicality. This is a simple choice; we have chosen (3).

If you want (2), you can get it in C # 4; dynamic starts the compiler again at runtime and finds out if there is an explicit conversion statement.

Why is an indirect transfer from T to D through a legal entity?

Because now no user transformation can be relevant; there is never a custom conversion from an object to something.

Why is the indirect transfer from T to D through I legal?

Because now no user transformation can be relevant; never will the user determine the conversion from the interface to anything.

Bonus question:

But D doesn't even implement I ! What's up with that?

Derived class D can:

 class F : D, I {} ... EM<D>(new F()); 

Now T can be added to I because it can implement I , and I can be added to D because it can be F

If D were sealed , then it would not be legal to drop I to D , because then there could be no derived type F

+16
source

You can convert an object using (TService)(Object)instance or use constraints for a general parameter.

The reason the compiler is happy with the act is simply because it does not know what is behind the interface instance, so the cast can be successful. When casting from a class to a generic type T, it does not know if the stock is valid and throws an error at compile time. Of course, he could have done it after compilation, but there is a good reason for this, imagine ...

 return (TService)myinstance; 

exchanges:

 return (CustomService)myinstance; 

and the myinstance variable is myinstance compatible with the CustomService type.

But TService different from an interface to a TService can succeed from either an Object to a TService .

+2
source

Have you tried to add a restriction for IAppSession ?

 public virtual TService GetService<TService>() where TService : IAppSession, class 

This related question is exactly the same problem. The compiler does not know that TService can be an AppSession .

+2
source

Source: https://habr.com/ru/post/1399031/


All Articles