Casting constrained generic class in C# - c#

Quite simply, why does this code fail to compile?
public interface IWorld { }
public class Foo<T> where T : IWorld { }
public void Hello<T>(T t) where T : IWorld
{
Foo<IWorld> bar1 = new Foo<T>(); //fails implicit cast
Foo<IWorld> bar2 = (Foo<IWorld>)new Foo<T>(); //fails explicit cast
}
Since every T implements IWorld, every instance of Foo<T> should match Foo<IWorld>. Why not? Is there any way around this? I really don't want to resort to generics to accomplish this.

T : IWorld
means that T has been implemented IWorld and does not mean that it ONLY has implemented IWorld and EXACTLY is IWorld. It may also has been implemented other interfaces.
However, C# supports this cast in it's later versions. Please see http://msdn.microsoft.com/en-us/library/dd799517.aspx (Covariance and Contravariance in Generics)

You can cast to object first
Foo<IWorld> bar2 = (Foo<IWorld>)(object)new Foo<T>();

An even simpler objection - imagine that instead of Foo, this was, say List.
Having converted your List<T> to a List<IWorld>, I can now add some other IWorld implementing object (say of type T2) to a list that is constrained to only contain objects of type T. That shouldn't be valid.
So back to your Foo object - if it contains any methods that expect to be called with objects of type T, I can now call them with any object that implements IWorld - even if (imagine an additional type constraint of Foo) that object would not be an eligible type for Foo.
My point in the comments re: value types. Again, this may be easier if we talk in terms of List<T> - a List<T> for value types contains the value types without boxing. If you want a List<IWorld> of these same values, each value has to be boxed before it's added to the list.

What is the problem with following
Foo<IWorld> bar1 = new Foo<IWorld>();
What are you trying to achieve?
If you need to pass IWorld instance, you can safely pass T, but that is not the case in your code.
EDIT (Based on comments)
To cast to Foo<Array of something> you can use Cast or OfType depending on your requirement(whether you want to throw or ignore incompatible matches).
If it is .NET 4, it should work automatically due to CoVariance feature.

Related

Covariance and Contravariance with Func in generics

I need more information about variance in generics and delegates. The following code snippet does not compile:
Error CS1961 Invalid variance: The type parameter 'TIn' must be
covariantly valid on 'Test.F(Func)'. 'TIn' is
contravariant.
public interface Test<in TIn, out TOut>
{
TOut F (Func<TIn, TOut> transform);
}
The .net Func definition is as follows:
public delegate TResult Func<in T, out TResult> (T arg);
Why the compiler complains about TIn being contravariant and TOut - covariant while the Func expects exactly the same variance?
EDIT
The main constraint for me is that I want my Test interface to have TOut as covariant in order to use it something like this:
public Test<SomeClass, ISomeInterface> GetSomething ()
{
return new TestClass<SomeClass, AnotherClass> ();
}
Given that public class AnotherClass : ISomeInterface.
I need more information about variance in generics and delegates.
I wrote an extensive series of blog articles on this feature. Though some of it is out of date -- since it was written before the design was finalized -- there's lots of good information there. In particular if you need a formal definition of what variance validity is, you should carefully read this:
https://blogs.msdn.microsoft.com/ericlippert/2009/12/03/exact-rules-for-variance-validity/
See my other articles on my MSDN and WordPress blogs for related topics.
Why the compiler complains about TIn being contravariant and TOut - covariant while the Func expects exactly the same variance?
Let's slightly rewrite your code and see:
public delegate R F<in T, out R> (T arg);
public interface I<in A, out B>{
B M(F<A, B> f);
}
The compiler must prove that this is safe, but it is not.
We can illustrate that it is not safe by supposing that it is, and then discovering how it can be abused.
Let's suppose we have an Animal hierarchy with the obvious relationships, eg, Mammal is an Animal, Giraffe is a Mammal, and so on. And let's suppose that your variance annotations are legal. We should be able to say:
class C : I<Mammal, Mammal>
{
public Mammal M(F<Mammal, Mammal> f) {
return f(new Giraffe());
}
}
I hope you agree this is a perfectly valid implementation. Now we can do this:
I<Tiger, Animal> i = new C();
C implements I<Mammal, Mammal>, and we've said that the first one can get more specific, and the second can get more general, so we've done that.
Now we can do this:
Func<Tiger, Animal> f = (Tiger t) => new Lizard();
That's a perfectly legal lambda for this delegate, and it matches the signature of:
i.M(f);
And what happens? C.M is expecting a function that takes a giraffe and returns a mammal, but it's been given a function that takes a tiger and returns a lizard, so someone is going to have a very bad day.
Plainly this must not be allowed to happen, but every step along the way was legal. We must conclude that the variance itself was not provably safe, and indeed, it was not. The compiler is right to reject this.
Getting variance right takes more than simply matching the in and out annotations. You've got to do so in a manner that does not allow this sort of defect to exist.
That explains why this is illegal. To explain how it is illegal, the compiler must check that the following is true of B M(F<A, B> f);:
B is valid covariantly. Since it is declared "out", it is.
F<A, B> is valid contravariantly. It is not. The relevant portion of the definition of "valid contravariantly" for a generic delegate is: If the ith type parameter was declared as contravariant, then Ti must be valid covariantly. OK. The first type parameter, T, was declared as contravariant. Therefore the first type argument A must be valid covariantly. But it is not valid covariantly, because it was declared contravariant. And that's the error you're getting. Similarly, B is also bad because it must be valid contravariantly, but B is covariant. The compiler does not go on to find additional errors after it finds the first problem here; I considered it but rejected it as being a too-complex error message.
I note also that you would still have this problem even if the delegate were not variant; nowhere in my counterexample did we use the fact that F is variant in its type parameters. A similar error would be reported if we tried
public delegate R F<T, R> (T arg);
instead.
Variance is about being able to replace type parameters with either more or less derived types than originally declared. For example, IEnumerable<T> is covariant for T, meaning if you start with a reference to a IEnumerable<U> object, you can assign that reference to a variable having type IEnumerable<V>, where V is assignable from U (e.g. U inherits V). This works, because any code trying to use the IEnumerable<V> wants to receive only values of V, and since V is assignable from U, receiving only values of U is also valid.
For covariant parameters like T, you have to assign to a type where the destination type is the same as T, or assignable from T. For contravariant parameters, it has to go the other way. The destination type has to be the same as, or assignable to, the type parameter.
So, how does the code you are trying to write work in that respect?
When you declare Test<in TIn, out TOut>, you are promising that it will be valid to assign an instance of that interface Test<TIn, TOut> to any destination having the type Test<U, V> where U can be assigned to TIn and TOut can be assigned to V (or they are identical, of course).
At the same time, let's consider what your transform delegate is to expect. The Func<T, TResult> type variance requires that if you want to assign that value to something else, it also meets the variance rules. That is, a destination Func<U, V> must have U assignable from T, and TResult assignable from V. This ensures that your delegate target method which is expecting to receive a value of U will get one of those, and the value returned by the method, having type V, can be accepted by the code receiving it.
Importantly, your interface method F() is the one doing the receiving! The interface declaration promises that TOut will be used only as output from the interface members. But through the use of the transform delegate, the method F() will receive a value of TOut, making that input to the method. Likewise, the method F() is allowed to pass a value of TIn to the transform delegate, making that an output of your interface implementation, even though you've promised that TIn is used only as input.
In other words, every layer of call reverses the sense of the variance. Members in the interface have to use covariant type parameters as output only and contravariant parameters as input only. But those parameters become reversed in sense when they are used in delegate types passed to or returned from interface members, and have to comply with the variance in that respect.
A concrete example:
Suppose we have an implementation of your interface, Test<object, string>. If the compiler were to allow your declaration, you'd be permitted to assign a value of that implementation Test<object, string> to a variable having the type Test<string, object>. That is, the original implementation promises to allow as input any thing having type object and return only values having the type string. It's safe for code declared as Test<string, object> to work with this, because it will pass string objects to an implementation that requires objects values (string is an object), and it will receive values having the type object from an implementation that returns string values (again, string is an object, so also safe).
But your interface implementation expects code to pass a delegate of type Func<object, string>. If you were allowed to treat (as above) your interface implementation as a Test<string, object> instead, then the code using your re-cast implementation would be able to pass a delegate of Func<string, object> to the method F(). The method F() in the implementation is allowed to pass any value of type object to the delegate, but that delegate, being of type Func<string, object>, is expecting only values having the type string to be passed to it. If F() passes something else, e.g. just a plain old new object(), the delegate instance won't be able to use it. It's expecting a string!
So, in fact, the compiler is doing exactly what it's supposed to: it's preventing you from writing code that is not type-safe. As declared, if you were permitted to use that interface in a variant way, you would in fact be able to write code that while allowed at compile-time, could break at run-time. Which is the exact opposite of the whole point of generics: to be able to determine at compile-time that the code is type-safe!
Now, how to solve the dilemma. Unfortunately, there's not enough context in your question to know what the right approach is. It's possible that you simply need to give up on variance. Often, there's not actually any need to make types variant; it's a convenience in some cases, but not required. If that's the case, then just don't make the interface's parameters variant.
Alternatively, it's possible you really do want the variance and thought it would be safe to use the interface in a variant way. That's harder to solve, because your fundamental assumption was just incorrect and you will need to implement the code some other way. The code would compile if you could reverse the parameters in the Func<T, TResult>. I.e. make the method F(Func<TOut, TIn> transform). But there's not anything in your question that suggests that's actually possible in your scenario.
Again, without more context it's impossible to say what "other way" would work for you. But, hopefully now that you understand the hazard in the code the way you've written it now, you can revisit the design decision that led you to this not-type-safe interface declaration, and can come up with something that works. If you have trouble with that, post a new question that provides more detail as to why you thought this would be safe, how you're going to use the interface, what alternatives you've considered, and why none of those work for you.
TIn = the class knows how to read it, and the implementation is allowed to treat it as a type that is less derived than it actually is. You might pass it an instance that is more derived than expected, but that doesn't matter, because the derived class can do everything that the base class can do.
TOut = the implementation knows to to produce one, and the implementation is allowed to produce a type that is more derived than the caller is expecting. Again, it doesn't matter-- the caller can assign a more derived class to a less derived variable with no problem.
But--
If you pass the class a Func<TIn, TOut>, and you expect the class to be able to call it, then the class will have to be able to produce a TIn and read the TOut. Which is the opposite of the above.
Why can't it? Well, I already mentioned that the class can treat TIn as something that is less derived. If it attempts to call the function with an argument that is less derived, it won't work (what if the function is expecting to be able to call string.Length but the class passes it an object?). Also, if it attempts to read the results of the function as something that is more derived, that will fail as well.
You can eliminate the problem by eliminating the variance-- get rid of the in and out keywords-- which will render the class unable to substitute less/more derived types (this is called "invariance") but will allow you to both read and write the types.
Remove in and out -keywords from the interface definition:
public interface Test<TIn, TOut>{
TOut F (Func<TIn, TOut> transform);
}
remove the in and out key words:
public interface Test<TIn, TOut>
{
TOut F (Func<TIn, TOut> transform);
}
you can read about the meaning of them here:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-generic-modifier
A type can be declared contravariant in a generic interface or delegate if it is used only as a type of method arguments and not used as a method return type
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-generic-modifier
The type parameter is used only as a return type of interface methods and not used as a type of method arguments.

Contravariance/Covariance, why can't cast this?

Let's face it, I am still having some difficulties to understand the constrains when it's time to use covariance and contravariance in generics.
I wonder, why if I have this:
public interface IFasterListCov<out T>
{}
public interface IFasterListCon<in T>
{}
public class FasterList<T> : IList<T>, IFasterListCov<T>, IFasterListCon<T>
The third cast fails:
public void QueryNodes<T>() where T:INode
{
//somehow I can convert IFasterListCon<INode> to IFasterListCon<T>
IFasterListCon<INode> nodes = (IFasterListCon<INode>)_nodesDB[type];
//I guess this works because _nodesDB[type] is indeed a FasterList<T> object
//note: I am wrong, I can cast whatever INode implementation, not just T, which made me very confused :P
IFasterListCon<T> nodesT = (IFasterListCon<T>)nodes;
//I can't cast IFasterListCon<T> back to FasterList<T>
FasterList<T> nodeI = nodesT as FasterList<T>; //null
}
Dictionary<Type, IFasterListCov<INode>> _nodesDB;
to be clear _nodesDB[type] is a FasterList<T> declared through IFasterListCov<INode>
MyType : IMyType does not make Generic<IMyType> and Generic<MyType> related in any way.
In your particular case it is likely that nodesT is FasterList<Node> which is not FasterList<INode>.
Note that this conversion work for interface which support variance (co/contra) when you can specify in/out as you see in successful conversion to interface. See one of many questions for details - i.e. Generic Class Covariance.
There is also excellent answer about List co-variance - C# variance problem: Assigning List<Derived> as List<Base> which shows that List<Derived> and List<Base> can't be cast between each other:
List<Giraffes> giraffes = new List<Giraffes>();
List<Animals> animals = new List<Animals>() {new Lion()};
(giraffes as List<Animals>).Add(new Lion()); // What? Lion added to Girafes
Giraffe g = (animals as List<Giraffes>)[0] ; // What? Lion here?
In the scenario where you're calling QueryNodes<MyNode>, in order for your last cast to get a non-null value, the actual instance that you get with _nodesDB[type] must be a FasterList<MyNode>. It's not good enough for it to be FasterList<SomeOtherMostlyCompatibleNode>.
The runtime is very strict about types, it keeps track of the actual runtime types of everything involved, it's not good enough for the data types to be similar, or for you to only have MyNode objects populating your FasterList<SomeOtherMostlyCompatibleNode>, or anything else. If the types are not exactly what they should be, you need to do some sort of programmatic conversion, not just cast.

Covariance with C# Generics

Given an interface IQuestion and an implementation of that interface AMQuestion, suppose the following example:
List<AMQuestion> typed = new List<AMQuestion>();
IList<IQuestion> nonTyped = typed;
This example yields, as expected, a compile error saying the two are not of the same type. But it states an explicit conversion exists. So I change it to look like this:
List<AMQuestion> typed = new List<AMQuestion>();
IList<IQuestion> nonTyped = typed as IList<IQuestion>;
Which then compiles but, at run time, nonTyped is always null. If someone could explain two things:
Why this doesn't work.
How I can achieve the desired effect.
It would be greatly appreciated. Thank you!
The fact that AMQuestion implements the IQuestion interface does not translate into List<AMQuestion> deriving from List<IQuestion>.
Because this cast is illegal, your as operator returns null.
You must cast each item individually as such:
IList<IQuestion> nonTyped = typed.Cast<IQuestion>().ToList();
Regarding your comment, consider the following code, with the usual cliché animal examples:
//Lizard and Donkey inherit from Animal
List<Lizard> lizards = new List<Lizard> { new Lizard() };
List<Donkey> donkeys = new List<Donkey> { new Donkey() };
List<Animal> animals = lizards as List<Animal>; //let's pretend this doesn't return null
animals.Add(new Donkey()); //Reality unravels!
if we were allowed to cast List<Lizard> to a List<Animal>, then we could theoretically add a new Donkey to that list, which would break inheritance.
Why it doesn't work: as returns null if the value's dynamic type cannot be casted to the target type, and List<AMQuestion> cannot be casted to IList<IQuestion>.
But why can't it? Well, check it:
List<AMQuestion> typed = new List<AMQuestion>();
IList<IQuestion> nonTyped = typed as IList<IQuestion>;
nonTyped.Add(new OTQuestion());
AMQuestion whaaaat = typed[0];
IList<IQuestion> says "You can add any IQuestion to me". But that's a promise it couldn't keep if it were a List<AMQuestion>.
Now, if you didn't want to add anything, just view it as a collection of IQuestion-compatible things, then the best thing to do would be to cast it to an IReadOnlyList<IQuestion> with List.AsReadOnly. Since a read-only list can't have strange things added to it, it can be casted properly.
The issue is that List<AMQuestion> cannot be cast to IList<IQuestion>, so using the as operator does not help. Explicit conversion in this case means to cast AMQuestion to IQuestion:
IList<IQuestion> nonTyped = typed.Cast<IQuestion>.ToList();
By the way, you have the term "Covariance" in your title. In IList the type is not covariant. This is exactly why the cast does not exist. The reason is that the IList interface has T in some parameteres and in some return values, so neither in nor out can be used for T. (#Sneftel has a nice example to show why this cast is not allowed.)
If you only need to read from the list, you can use IEnumerable instead:
IEnumerable<IQuestion> = typed;
This will work because IEnumerable<out T> has out defined, since you can't pass it a T as parameter. You should usually make the weakest "promise" possible in your code to keep it extensible.
IList<T> is not covariant for T; it can't be, as the interface defines functions that take values of type T in an "input" position. However, IEnumerable<T> is covariant for T. If you can limit your type to IEnumerable<T>, you can do this:
List<AMQuestion> typed = new List<AMQuestion>();
IEnumerable<IQuestion> nonTyped = typed;
This does not do any conversions on the list.
The reason you cannot convert a List<AMQuestion> to a List<IQuestion> (assuming AMQuestion implements the interface) is that there would have to be several runtime checks on functions like List<T>.Add, to make sure you were really adding an AMQuestion.
The "as" operator will always return null there as no valid cast exists - this is defined behavior. You have to convert or cast the list like this:
IList<IQuestion> nonTyped = typed.Cast<IQuestion>().ToList();
A type with a generic type parameter can only be covariant if this generic type occurs only in read accesses and contravariant, if it occurs only in write accesses. IList<T> allows both, read and write access to values of type T, so it cannot be variant!
Let's assume that you were allowed to assign a List<AMQuestion> to a variable of type IList<IQuestion>. Now let’s implement a class XYQuestion : IQuestion and insert a value of that type into our IList<IQuestion>, which seems perfectly legal. This list still references a List<AMQuestion>, but we cannot insert a XYQuestion into a List<AMQuestion>! Therefore the two list types are not assignment compatible.
IList<IQuestion> list = new List<AMQuestion>(); // Not allowed!
list.Add(new XYQuestion()); // Uuups!
Because List<T> is not a sealed class, it would be possible for a type to exist which would inherit from List<AMQuestion> and implement IList<IQuestion>. Unless you implement such a type yourself, it's extremely unlikely that one will ever actually exist. Nonetheless, it would be perfectly legitimate to say, e.g.
class SillyList : List<AMQuestion>, IList<IQuestion> { ... }
and explicitly implement all the type-specific members of IList<IQuestion>. It would thus also be perfectly legitimate to say "If this variable holds a reference to an instance of a type derived from List<AMQuestion>, and if that instance's type also implements IList<IQuestion>, convert the reference to the latter type.

Generic type casting

If i have a type and an object eg.:
- Type someType (coming from somewhere, could be any class eg. MyClass.GetType())
- Object someObject (eg. List<MyClass>())
and want to cast the object back to List<MyClass>. How should i do this?
You can't do this. Generics ensure compile-time safety. You cannot have compile time safety because you know the actual type only at runtime.
You have a runtime type and you want to perform a compile time cast. This is not possible. It is also not clear why you would want to do this in the first place. If you are interested in cases that require reflection, perhaps you should investigate that topic further.
There is no way to have compile-time typing a variable when you only receive the Type information at runtime.
This is different from generics since in generics you get the type information at compile time:
void MyFunc<T>(T thing)
{
// T is passed in at compile time
}
In your case you are getting the type at runtime. So while you can't cast the member to the type the way you normally would you can reflect on the instance and call its members:
void MyFunc(object thing, Type type)
{
var res = t.GetMethod("Add").Invoke(a, new []{"someArg"});
}
Casting means explicitly specifying the type you want to convert to. Since you don't know what your type is, you can't cast to it.
That doesn't mean you can't access the list. If you know the object you have is a list of something, you can cast it to the non-generic IList interface, which provides most of the methods and properties you need:
object obj = GetMyList();
IList list = (IList)obj;
object fifthItem = list[4];
list.RemoveAt(list.Count - 1);
If you describe the problem you're trying to solve rather than the solution you are trying to achieve, then more fitting solutions might be posted.
If you are trying to cast a runtime type at compile time, it is impossible as may said before me.
However, you could cheat a little (but don't use this technique excessively, it leads down a dark road...)
public void DoSomething<T>(List<T> object) where T : ....
{
//do what you want here
}
public void CallIt(Type t, object o) //o is List<Foo>
{
this.GetType().GetMethod("DoSomething").MakeGenericMethod(t).Invoke(o);
}
However I don't see any real benefit to this, as if you don't write any type constraint you gain nothing with using generics instead of objects and IList interface, and if you write any baseclass or interface there, you could just cast your object to that. (For example if you know that T implements IFoo, you could cast o to IList<IFoo> and have every benefit of List<Foo>...

Generic method to cast one arbitrary type to another in c#

I want to do something like this:
public static TResult MyCast<TSource, TResult>(TSource item)
{
return (TResult)item;
}
Without restrictions on TSource or TResult and avoiding unnecessary boxing if possible.
Edit: I want to stress out, that I want a simple casting of types, not elaborate type conversion here. It would be perfectly ok to fail at casting, say string to int.
Is there any sane way to do this using CLR 2.0?
Edit: this is a simplified version, so it's pretty useless, yes.
But consider casting generic collections, such as this:
public static Dictionary<string, TResult> CastValues<TSource, TResult>(this Dictionary<string, TSource> dictionary)
After some discussions with my co-workers, it seems like there's no simple way to implement such a feature (if at all possible), so I'm stuck with code bloat of several very simple methods for different situations (i.e. up- and downcast of reference types and casting of some value types) :(
Too bad I can't use .NET 4.0 with all it's dynamic et al goodness.
How would
x = MyCast<SourceType, ResultType>(y)
be any more useful than
x = (ResultType)y ?
This is straightforward when TSource and TResult are both reference types.
If one or the other are value types, how do you want it to work? Value types can't inherit from each other, so it's not a matter of doing an up- or down-cast. You might expect numeric conversions between, say, int and double, but you'd have to code these yourself: .NET doesn't treat them as typecasts. And conversion between, say, DateTime and string involves more intelligence (what format? which culture? etc.).
If you're just handling reference types then this method can be a one-liner. If you want to handle value types as well then you'll need to write special case code for the various combinations.
Edit: Convert.ChangeType does a reasonable job at encapsulating the various conversions between value types. However you mentioned you're keen not to introduce boxing: Convert.ChangeType isn't generic and it takes an object.
I think that the problem you are trying to solve is the same as the problem that you cannot cast a collection of one type to a collection of another type.
eg
class Obj1
{}
class Obj2:Obj1
{}
List<Obj2> srcList = GetList();
List<Obj1> castedList=(List<Obj2>) srcList;//this line wont compile
I have not done much at actually looking at the CLR code
However on the asuumption that it is like C++ what you would have here is actually different values stored in the collection. In other words srcList would contain a list of pointers to object 2's interface in castedList you would have a pointer to the the interface of the object 1's within object 2.
In order to resolve this you would need to have your casting function iterate through each of the items within the collection. However in order to be able to iterate through the items the list would have to implement some sort of enumeration interface. So the enumeration interface would need to be a constraint on the casting function.
So the answer would therefore be no.
However if you were prepared to implement this with restrictions on the in types you could have:
static class ListCast<TSource,TResult,TItemType>
where TSource:IEnumerable<TItemType>
where TResult:IList<TItemType>,new()
{
static TResult Cast(TSource list)
{
TResult castedList=newTResult();
foreach(TtemType item in list)
{
castedList.Add(TItemType)item);
}
return castedList;
}
}
you can just do this:
public static TResult MyCast<TSource, TResult>(TSource item)
{
return (TResult)((object)item);
}
Would love to hear how this could be bad.

Categories