C# variance problem: Assigning ListDerived as ListBase
Well this certainly won't be supported in C# 4. There's a fundamental problem:
List<Giraffe> giraffes = new List<Giraffe>();
giraffes.Add(new Giraffe());
List<Animal> animals = giraffes;
animals.Add(new Lion()); // Aargh!
Keep giraffes safe: just say no to unsafe variance.
The array version works because arrays do support reference type variance, with execution time checking. The point of generics is to provide compile-time type safety.
In C# 4 there will be support for safe generic variance, but only for interfaces and delegates. So you'll be able to do:
Func<string> stringFactory = () => "always return this string";
Func<object> objectFactory = stringFactory; // Safe, allowed in C# 4
Func<out T>
is covariant in T
because T
is only used in an output position. Compare that with Action<in T>
which is contravariant in T
because T
is only used in an input position there, making this safe:
Action<object> objectAction = x => Console.WriteLine(x.GetHashCode());
Action<string> stringAction = objectAction; // Safe, allowed in C# 4
IEnumerable<out T>
is covariant as well, making this correct in C# 4, as pointed out by others:
IEnumerable<Animal> animals = new List<Giraffe>();
// Can't add a Lion to animals, as `IEnumerable<out T>` is a read-only interface.
In terms of working around this in your situation in C# 2, do you need to maintain one list, or would you be happy creating a new list? If that's acceptable, List<T>.ConvertAll
is your friend.
C#, How to pass a List of a Derived class to a method that receives a List of the Base class?
The reason for such behaviour is explained here. In short - classes do not support variance in C# and List<AppleBox>
is not List<FruitBox<Apple>>
.
What you can do:
- "convert" collection (actually create a new one):
with OfType<>().ToList()
AppleBox.ChooseFirst(appleBoxes.OfType<FruitBox<Apple>>().ToList())
or just ToList
AppleBox.ChooseFirst(appleBoxes.ToList<FruitBox<Apple>>())
- change
ChooseFirst
signature to work with covariantIEnumerable<out T>
interface:
public abstract class FruitBox<T>
{
public T item;
public static T ChooseFirst(IEnumerable<FruitBox<T>> fruitBoxes)
{
return fruitBoxes.First().item;
}
}
Contra/Covariance issue when assigning to Lazy
If there were an ILazy<T>
interface, it could be declared as ILazy<out T>
, and all would be well in your example: the T
is only used in an output position, effectively.
However, Lazy<T>
is a class. Covariance/contravariance can only be specified for delegates and interfaces, so Lazy<T>
can't specify its covariance.
Therefore Lazy<Control>
is incompatible with Lazy<T>
, which is why the assignment isn't working. It's definitely frustrating, given that with the current API at least, it would be "safe" for it to be covariant, from a caller perspective.
(If you need this all over the place, you could declare your own ILazy<out T>
interface and then write an implementation of that interface to wrap a Lazy<T>
. I suspect that's more trouble than it's worth though.)
Why can't I assign a ListDerived to a ListBase?
The simplest way to understand why this is not allowed is the following example:
abstract class Fruit
{
}
class Apple : Fruit
{
}
class Banana : Fruit
{
}
// This should intuitively compile right? Cause an Apple is Fruit.
List<Fruit> fruits = new List<Apple>();
// But what if I do this? Adding a Banana to a list of Apples
fruits.Add(new Banana());
The last statement would ruin the type safety of .NET.
Arrays however, do allow this:
Fruit[] fruits = new Apple[10]; // This is perfectly fine
However, putting a Banana
into fruits
would still break type safety, so therefor .NET has to do a type check on every array insertion and throw an exception if it's not actually an Apple
. This is potentially a (small) performance hit, but this can be circumvented by creating a struct
wrapper around either type as this check does not happen for value types (because they can't inherit from anything). At first, I didn't understand why this decision was made, but you'll encounter quite often why this can be useful. Most common is String.Format
, which takes params object[]
and any array can be passed into this.
In .NET 4 though, there's type safe covariance/contravariance, which allows you to make some assignments like these, but only if they're provably safe. What's provably safe?
IEnumerable<Fruit> fruits = new List<Apple>();
The above works in .NET 4, because IEnumerable<T>
became IEnumerable<out T>
. The out
means that T
can only ever come out of fruits
and that there's no method at all on IEnumerable<out T>
that ever takes T
as a parameter, so you can never incorrectly pass a Banana
into IEnumerable<Fruit>
.
Contravariance is much the same but I always forget the exact details on it. Unsurprisingly, for that there's now the in
keyword on type parameters.
Cannot convert object of base instance to derived type (generics usage)
This issue has nothing to do with variance and generics. Next code will give you the same error:
class Interval1
{
public static Interval1 Empty = new Interval1();
}
class DateTimeInterval1 : Interval1
{
}
DateTimeInterval1 p = (DateTimeInterval1)DateTimeInterval1.Empty;
Reason being that Empty
is instance of Interval
not DateTimeInterval
, so it can't be cast to DateTimeInterval
.
Related Topics
Convert Utc/Gmt Time to Local Time
How to Convert Json to Xml or Xml to Json
How to Specify a [Dllimport] Path At Runtime
Possible to Call C++ Code from C#
Easiest Way to Read from and Write to Files
What's a "Static Method" in C#
Is Is Possible to Export Functions from a C# Dll Like in VS C++
What Does the Unary Plus Operator Do
An Existing Connection Was Forcibly Closed by the Remote Host
Pinvokestackimbalance - How to Fix This or Turn It Off
How to Call a C# Library from Native C++ (Using C++\Cli and Ijw)