In developing a class that should handle various generic lambda expressions, I fell into a rather familiar hole: I had a MyClass<T> class, and I tried to cast MyClass<string> to MyClass<object>, like so:
MyClass<string> myClassString = GetMyClass(); // Returns MyClass<String>
MyClass<object> myClassObject = myClassString;
Doing so returned an compilation error saying there there's no implicit conversion between the two types, but that an explicit conversion does exist. So I added the explicit conversion:
MyClass<object> myClassObject = (MyClass<object>)myClassString;
Now the code compiled, but failed in runtime, claiming the conversion is illegal.
I am using Visual Studio 2012, and the code is part of a Portable Class Library project compiled with C# 5.
Just to make sure, I replaced MyClass IList - the same behavior appeared - an explicit conversion is allowed, but fails during run-time.
Why? Why does the compiler accept this? What's the point of the explicit conversion if it fails in runtime?
In order to allow the cast, you need to mark it as covariant. However, covariance is only allowed for interfaces (and delegates). This would look like:
interface MyInterface<out T> ...
The reason why you can compile the explicit cast is probably that the compiler assumes that the return value of GetMyClass() could be a MyClass<object>. But That's hard to say without the declaration.
To be able to cast MyClass<string> to MyClass<object> you need to fulfill the following:
MyClass<T> must be an interface, e.g. IMyClass<T>.
You need to add the out keyword to the type parameter T, making the type parameter covariant.
The type parameter T may only appear in output positions of the members of the interface.
For example:
public interface IMyClass<out T>
{
T GetItem(); // T in an output position
}
Now you can cast it:
IMyClass<string> myClassString;
IMyClass<object> myClassObject = (IMyClass<object>)myClassString;
If you have classes A and B and you want to cast B to A then one of the following must be true:
B derives/implements A or vice versa
There is an explicit cast operator defined from B to A
None of the above is true in your case so the cast is invalid.
If your class implements IEnumerable you may use an extension method
using System.Linq
...
MyClass<Object> asObject = GetMyClass().Cast<Object>();
Otherwise, write an explicit operator or function that does the conversion:
public MyClass<Base> ConvertToBase<Base, Derived>(MyClass<Derived>) where Derived : Base
{
// construct and return the appropriate object
}
use Conversion operator to implement casting of your class to another classes
http://msdn.microsoft.com/en-us/library/85w54y0a.aspx
http://msdn.microsoft.com/en-us/library/09479473(v=vs.80).aspx
Related
I have some code that I have no control of. This code accepts an object parameter and attempts to cast it to a type known at compile time like this:
KnownType item = (KnownType) parameter;
Is it possible in C# to design a custom class MyClass (not derived from KnownType) that can be passed as parameter to the above code and be converted to KnownType by the above code, provided that MyClass can convert itself to KnownType using its member method:
protected KnownType ConvertToKnownType()
{
// conversion code goes here
}
I have tried to implement a custom conversion operator like this:
public static implicit operator KnownType(MyClass source)
{
KnownType result;
// conversion goes here
return result;
}
but it didn't work (it was not used). Am I right to assume that the cast operator only works when the source type, target type and conversion operators are known at compile time?
Edit:
I initially didn't provide more details about the code doing the conversion because I think it is irrelevant and because I am mainly interested in the way the cast operator is implemented, i.e. does it take look at the runtime type to find an appropriate converter or is the decision made at compile time?
To clear things out, KnownType is in fact DataRowView, while MyClass is a wrapper class for DataRowView that has to derive from some other type. MyClass keeps a reference to a DataRowView. Instead of binding ComboBox.DataSource to a DataView, I bind it to an IList<MyClass> but I still need the ComboBox to be able to access the DataRowView column values as if I was binding an IList<DataRowView>. Unfortunately the cast operator works the way I was afraid it would: it only takes into account compile time type information to do conversions (it does however use runtime type information when casting between types in the same inheritance tree).
No, it is not possible. The cast provided would only succeed if you're able to derive your class from that class. Any type of conversion not based on inheritance would require the class performing the conversion to do something different than what it's doing.
Am I right to assume that the cast operator only works when the source type, target type and conversion operators are known at compile time?
Yes.
static IEnumerable<U> DoSomething<T, U>(IEnumerable<T> a)
where T : U
{
// Works, compiler can compile-time statically cast
// T as U.
T testA = default(T);
U testB = testA;
// And the following works, though:
IEnumerable<string> test2A = null;
IEnumerable<object> test2B = test2A;
// Doesn’t work. Compiler cannot compile-time statically
// cast IEnumerable<T> to IEnumerable<U> even though it is
// out and T is U.
return a;
}
I have code where being able to perform this type of implicit cast would save me writing a lot of boilerplate interface implementation code.
This seems to be the sort of thing which covariance was supposed to help with.
But I always get this error on the return a; line above:
error CS0266: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<T>' to 'System.Collections.Generic.IEnumerable<U>'. An explicit conversion exists (are you missing a cast?)
Why is this this way and is there a way to work around this without doing something like return from o in a select o;?
When messing around with my minimal repro and reading similar, but unrelated, question about interface casting, I realized that the following compiles:
static IEnumerable<U> DoSomethingElse<T, U>(IEnumerable<T> a)
where T : class, U
{
// Works! Ridik!
return a;
}
And also that the following fails with the same error message:
static void Blah()
{
// Fails for I bet the same reason that DoSomething{T, U} fails:
IEnumerable<int> a = null;
IEnumerable<object> b = a;
}
error CS0266: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<int>' to 'System.Collections.Generic.IEnumerable<object>'. An explicit conversion exists (are you missing a cast?)
So this seems to be related to how .net restricts certain types of assignments to reference types because boxing in these situations would either be the wrong thing (e.g., you might assume reference types and actually be working on a copy of a value type) or very hard/impossible to implement in the runtime (given an IEnumerable<int> you’d have to implement a wrapping adapting class. OK, so that sounds like something .net can’t/shouldn’t try to do for you at runtime). I think of it as a situation where .net allows pointer-style polymorphism which, by its very nature, is incompatible with the concept of value types.
So, for my case, I don’t need to support value types in my API here and adding the class constraint makes everything magical!
I was wondering if C# supported implicit type discovery for class generics.
For example, such functionaly exists on method generics.
I can have the following method:
public void Foo<T>(T obj);
And call it like this:
int n = 0;
instance.Foo(n);
As you can see, I'm not specifying the <int> generic constraint. It's being implicitly discovered, because I passed an int value.
I want to accomplish something similiar on a class definition level:
internal interface IPersistenceStrategy<E, T> : IDisposable
where E : UniqueEntity<T>
I want it to be defined as IPersistenceStrategy<MyEntity>, where MyEntity is an UniqueEntity<int>.
As you can see, the T type param, is being implicitly discovered from MyEntity.
However, this does not work. I have to supply the T param explicitly:
IPersistenceStrategy<MyEntity, int> myStrategy;
Why is this functionality not working? Is C# compiler not smart enough to discover my type param automatically?
Is there some way to accomplish what I am looking for?
There is no type inference in generic type declarations on initialization. You can only omit the generic argument when calling a generic method but it is not the case with initializing a generic type for example:
var list = new List { 2, 3, 4 };
Here you may expect compiler to see that you wanna create a list of int so there is no need to specify type argument.But it is not the case.
In your specific example let's assume compiler has inferred this :
IPersistenceStrategy<MyEntity> myStrategy;
as IPersistenceStrategy<MyEntity,int> then what should happen if there is another declaration in the same assembly such as:
interface IPersistenceStrategy<T> { }
Ofcourse this would cause an ambiguity. So that might be the one of the reasons why it is not allowed.
C# has type inference for methods, but not for constructors. This feature was proposed to be in C# 6 version, but seems was removed from release according to Mads Torgersen (http://blogs.msdn.com/b/csharpfaq/archive/2014/11/20/new-features-in-c-6.aspx).
Also have a look to Languages features in C# 6 and VB 14, i.e. there is no mention about it
Just came across this:
Func<List<object>> foo = () => new List<object>();
List<string> s = (List<string>)foo();
IList<string> s1 = (IList<string>)foo();
Compiler complains about casting to List (makes sense), but says nothing about IList. Makes me wonder why is that?
The compiler knows that a List<X> cannot be a List<Y>.
It therefore gives a compiler error.
However, the second cast could succeed if the List<X> is actually some derived class that also implements IList<Y>.
You will only get a compile-time error from a cast if neither type is an interface, or if one type is an unrelated interface and the other type is sealed (or a struct).
To quote the spec (§6.4.2)
The explicit reference conversions are:
From object and dynamic to any other reference-type.
From any class-type S to any class-type T, provided S is a base class of T.
From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T.
From any interface-type S to any class-type T, provided T is not sealed or provided T implements S.
From any interface-type S to any interface-type T, provided S is not derived from T.
[snip]
(emphasis added)
The provided... clauses exclude conversions that are actually implicit.
An object that is known to be a List<object> might implement IList<string> as well as IList<object>, so it's possible that the cast can succeed. It can't in this case because we know that the statement is simply new List<object>(), but the compiler doesn't consider that. You might've extended List<T> and implemented the other, e.g.
// not recommended, but second cast works
public class MyWeirdList : List<object>, IList<string>
An object that is known to be a List<object> cannot possibly also be a List<string>, because you can only inherit from a single type.
public class MyWeirdList : List<object>, List<string> // compiler error
If List<T> were sealed, both casts would be invalid, because then the compiler would know for sure that the class couldn't implement IList<string>. You can try this by using this class instead:
public sealed class SealedList<T> : List<T> { }
The first line fails at compile time, the second gives an "Unable to cast object of type 'System.Collections.Generic.List1[System.Object]' to type 'System.Collections.Generic.IList1[System.String]'." exception during runtime.
If you look at this question (Cast IList<string> to IList<object> fails at runtime), the answer clarifies why this compile works and also provides an example for a class that could satisfy the conditions provided.
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.
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 to D 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:
Make cast operators sometimes use explicit conversion operators and sometimes not, depending on whether you're in a generic or not.
Make cast operators start the compiler again at runtime to look for explicit conversion operators that might have been added in different assemblies after the original code was compiled.
Disallow the cast in the first place.
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 to D 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 to D via I 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 implement I! 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.
You can convert the object by using (TService)(Object)instance or use constraints on the generic parameter.
The reason why the compiler is happy about the cast, is simply that he doesn't know what is behind the instance of a interface so the cast may succeed. When casting from class to generic type T he doesn't know if the cast is valid and throws a error at compile time. Of course he could do this also after compile but there is a good reason for this, imagine...
return (TService)myinstance;
is exchanged by:
return (CustomService)myinstance;
and the myinstance variable is not compatible with the type CustomService.
But a cast from a interface to TService may succeed, or from Object to TService.