public class Animal
{
}
public class Cat : Animal
{
}
public class AnimalBag<T> where T : Animal
{
}
...
AnimalBag<Animal> bag = new AnimalBag<Cat>();
I get this error:
Cannot implicitly convert type AnimalBag<Cat> to `AnimalBag'
And if I try this:
AnimalBag<Animal> bag = (AnimalBag<Animal>) new AnimalBag<Cat>();
Cannot convert type AnimalBag<Cat> to AnimalBag<Animal>.
How can I resolve this kind of issues in C#? This works perfectly in Java.
The declaration AnimalBag<Animal> bag states that bag will be capable of storing any type of Animal. The assignment = new AnimalBag<Cat>(); contradicts this by saying that it is actually only capable of storing Cats. Which is it, as it cannot be both?
This case is where co-variant comes into play, but co-variant does not work with class, just only interface and delegate, so you need to define interface for co-variant using out keyword:
public interface IAnimalBag<out T> where T : Animal
{
}
public class AnimalBag<T> : IAnimalBag<T> where T: Animal
{
}
Then you can assign:
IAnimalBag<Animal> bag = new AnimalBag<Cat>();
You can check more co-variance and contra-variance in Generics
Related
I have a question on typing and contra/covairance.
Given the following classes
public class BoardItemsHolderRepository<THolder, TBoardItem> : DataRepository<IList<THolder>>
where TBoardItem : BoardItem
where THolder : IBoardItemHolder<TBoardItem>
{
}
public interface IDataRepository<T> : IDataGetRepository<T>, IDataSetRepository<T> where T : class
{
}
public interface IDataGetRepository<out T> where T : class
{
IObservable<T> GetObservableStream();
IObservable<T> GetMostRecent();
}
public interface IDataSetRepository<in T> where T : class
{
void Set(T value);
}
public abstract class DataRepository<T> : IDataRepository<T> where T : class
{
..implementation details
}
public class ConstructHolder : IBoardItemHolder<Construct>
{
..implementation details
}
Given the above 3 files, can someone explain to me why is the following happening?:
IDataGetRepository<IList<IBoardItemHolder<Construct>>> wontCompile = new BoardItemsHolderRepository<ConstructHolder, Construct>(); //illegal
IDataGetRepository<IList<ConstructHolder>> compile = new BoardItemsHolderRepository<ConstructHolder, Construct>(); //legal
I can't understand why implicit casting for the first line would not work, as the following line compiles(as expected)
IBoardItemHolder<Construct>> compile = new ConstructHolder();
Let's expand the right hand side of the illegal line step by step. First, we start with
BoardItemsHolderRepository<ConstructHolder, Construct>
This is a DataRepository<IList<THolder>>, so the above is a kind of:
DataRepository<IList<ConstructHolder>>
which in turn is IDataGetRepository<T>, so the above is a kind of:
IDataGetRepository<IList<ConstructHolder>>
IDataGetRepository<T> is covariant on T. Recall exactly what this means: If U is a subtype of T, then IDataGetRepository<U> is a subtype of IDataGetRepository<T>. For example, IDataGetRepository<Cat> is a subtype of IDataGetRepository<Animal>, and so an instance of the former type can be assigned to a variable of the latter type.
However, IDataGetRepository<IList<ConstructHolder>> is not a subtype of IDataGetRepository<IList<IBoardItemHolder<Construct>>> and so cannot be assigned to it. Why? Because IList<ConstructHolder> is not a subtype of IList<IBoardItemHolder<Construct>>! IList<T> is invariant on T!
So what you are trying to do violates type safety, according to the type-checker. Maybe try using a IEnumerable rather than IList?
abstract class BaseClassA{}
class DerivedClassA : BaseClassA{}
abstract class BaseClassB<T> where T : BaseClassA
{
public T foo;
}
class DerivedClassB : BaseClassB<DerivedClassA>
{
public DerivedClassB(DerivedClassA _foo)
{
foo = _foo;
}
}
class Test
{
DerivedClassB foobar = new DerivedClassB();
void main()
{
//Error: casting not possible
BaseClassA<BaseClassB> bar = new DerivedClassA<DerivedClassB>(foobar);
}
}
Hey everyone.
I have a little problem. The code above gives me a "cannot cast" Exception.
I want to be able to access foo of DerivedClassB without casting it from BaseClassA to DerivedClassA.
Can't I have a generic type derive from the Base Class and still use it?
The problem is that if what you want were legal then this would also be legal:
abstract class Animal {}
class Goldfish : Animal {}
class Giraffe : Animal {}
abstract class Cage<T> where T : Animal {
void Add(T newAnimal) { ... }
}
class Aquarium : Cage<Goldfish> { }
...
Cage<Animal> c = new Aquarium(); // This is not legal, but suppose it was
c.Add(new Giraffe()); // A giraffe is an animal
And now we have a giraffe in an aquarium, delighting no one.
This feature is called covariance and it only works on generic interface and delegates, when they are constructed with reference types, and they are designed specifically to handle variance and checked for safety by the compiler.
For example, you can use an IEnumerable<Fish> in a context where an IEnumerable<Animal> is expected because there is no way to add a giraffe to a sequence of animals.
Can't I have a generic type derive from the Base Class and still use it?
Yes but you have to follow the rules. Those rules are there to keep you safe. Learn how the type system works and work with it to help prevent your bugs, and you will never put a giraffe in an aquarium by mistake.
I have created a couple of interfaces and generic classes for working with agenda appointments:
interface IAppointment<T> where T : IAppointmentProperties
{
T Properties { get; set; }
}
interface IAppointmentEntry<T> where T : IAppointment<IAppointmentProperties>
{
DateTime Date { get; set; }
T Appointment { get; set; }
}
interface IAppointmentProperties
{
string Description { get; set; }
}
class Appointment<T> : IAppointment<T> where T : IAppointmentProperties
{
public T Properties { get; set; }
}
class AppointmentEntry<T> : IAppointmentEntry<T> where T : IAppointment<IAppointmentProperties>
{
public DateTime Date { get; set; }
public T Appointment { get; set; }
}
class AppointmentProperties : IAppointmentProperties
{
public string Description { get; set; }
}
I'm trying to use some constraints on the type parameters to ensure that only valid types can be specified. However, when specifying a constraint defining that T must implement IAppointment<IAppointmentProperties>, the compiler gives an error when using a class that is Appointment<AppointmentProperties>:
class MyAppointment : Appointment<MyAppointmentProperties>
{
}
// This goes wrong:
class MyAppointmentEntry : AppointmentEntry<MyAppointment>
{
}
class MyAppointmentProperties : AppointmentProperties
{
public string ExtraInformation { get; set; }
}
The error is:
The type 'Example.MyAppointment' cannot be used as type parameter 'T' in the generic type or method 'Example.AppointmentEntry<T>'. There is no implicit reference conversion from 'Example.MyAppointment' to 'Example.IAppointment<Example.IAppointmentProperties>'.
Could anybody explain why this does not work?
Let's simplify:
interface IAnimal { ... }
interface ICage<T> where T : IAnimal { void Enclose(T animal); }
class Tiger : IAnimal { ... }
class Fish : IAnimal { ... }
class Cage<T> : ICage<T> where T : IAnimal { ... }
ICage<IAnimal> cage = new Cage<Tiger>();
Your question is: why is the last line illegal?
Now that I have rewritten the code to simplify it, it should be clear. An ICage<IAnimal> is a cage into which you can place any animal, but a Cage<Tiger> can only hold tigers, so this must be illegal.
If it were not illegal then you could do this:
cage.Enclose(new Fish());
And hey, you just put a fish into a tiger cage.
The type system does not permit that conversion because doing so would violate the rule that the capabilities of the source type must not be less than the capabilities of the target type. (This is a form of the famous "Liskov substitution principle".)
More specifically, I would say that you are abusing generics. The fact that you've made type relationships that are too complicated for you to analyze yourself is evidence that you ought to simplify the whole thing; if you're not keeping all the type relationships straight and you wrote the thing then your users surely will not be able to keep it straight either.
There is already a very good answer from Eric. Just wanted to take this chance to talk about the Invariance, Covariance, and Contravariance here.
For definitions please see https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance
Let's say there is a zoo.
abstract class Animal{}
abstract class Bird : Animal{}
abstract class Fish : Animal{}
class Dove : Bird{}
class Shark : Fish{}
The zoo is relocating, so its animals need to be moved from the old zoo to the new one.
Invariance
Before we move them, we need to put the animals into different containers. The containers all do the same operations: put an animal in it or get an animal out from it.
interface IContainer<T> where T : Animal
{
void Put(T t);
T Get(int id);
}
Obviously, for fish we need a tank:
class FishTank<T> : IContainer<T> where T : Fish
{
public void Put(T t){}
public T Get(int id){return default(T);}
}
So the fish can be put in and get out from the tank(hopefully still alive):
IContainer<Fish> fishTank = new FishTank<Fish>(); //Invariance, the two types have to be the same
fishTank.Put(new Shark());
var fish = fishTank.Get(8);
Suppose we are allowed to change it to IContainer<Animal>, then you can accidentally put a dove in the tank, in which case tragedy will occur.
IContainer<Animal> fishTank = new FishTank<Fish>(); //Wrong, some animal can be killed
fishTank.Put(new Shark());
fishTank.Put(new Dove()); //Dove will be killed
Contravariance
To improve efficiency, the zoo management team decides to separate the load and unload process (management always does this). So we have two separate operations, one for load only, the other unload.
interface ILoad<in T> where T : Animal
{
void Put(T t);
}
Then we have a birdcage:
class BirdCage<T> : ILoad<T> where T : Bird
{
public void Put(T t)
{
}
}
ILoad<Bird> normalCage = new BirdCage<Bird>();
normalCage.Put(new Dove()); //accepts any type of birds
ILoad<Dove> doveCage = new BirdCage<Bird>();//Contravariance, Bird is less specific then Dove
doveCage.Put(new Dove()); //only accepts doves
Covariance
In the new zoo, we have a team for unloading animals.
interface IUnload<out T> where T : Animal
{
IEnumerable<T> GetAll();
}
class UnloadTeam<T> : IUnload<T> where T : Animal
{
public IEnumerable<T> GetAll()
{
return Enumerable.Empty<T>();
}
}
IUnload<Animal> unloadTeam = new UnloadTeam<Bird>();//Covariance, since Bird is more specific then Animal
var animals = unloadTeam.GetAll();
From the team's point of view, it does not matter what it is inside, they just unload the animals from the containers.
Because you declared your MyAppointment class using the concrete type rather than the interface. You should declare as follows:
class MyAppointment : Appointment<IAppointmentProperties> {
}
Now the conversion can occur implicitly.
By declaring AppointmentEntry<T> with the constraint where T: IAppointment<IAppointmentProperties> you are creating a contract whereby the unspecified type for AppointmentEntry<T> must accommodate any type that is declared with IAppointmentProperties. By declaring the type with the concrete class you have violated that contract (it implements a type of IAppointmentProperties but not any type).
It will work if you re-define the sample interface from:
interface ICage<T>
to
interface ICage<out T>
(please notice the out keyword)
then the following statement is correct:
ICage<IAnimal> cage = new Cage<Tiger>();
In case someone else also has this error message: I found the same interface defined twice in different namespaces and the classes that have been tryed to be linked together did not use the same interface.
I want something like this:
public interface IAnimal
{ }
public class Dog : IAnimal
{
public Dog() {}
}
public class Cat : IAnimal
{
public Cat() {}
}
public abstract class TestClassBase
{
public TestClassBase()
{
_lazyAnimal = CreateLazyAnimal();
}
private Lazy<IAnimal> _lazyAnimal = null;
public IAnimal Animal
{
get
{
IAnimal animal = null;
if (_lazyAnimal != null)
animal = _lazyAnimal.Value;
return animal;
}
}
// Could be overridden to support other animals
public virtual Lazy<IAnimal> CreateLazyAnimal()
{
// default animal is a dog
return new Lazy<Dog>(); // this type of casting doesn't work and I don't know a good workground
}
}
I know from tinkering with MEF that it manages to find and store different types, implementing a single interface, into Lazy<T>. Just not sure how to do it myself.
Lazy<Dog> cannot be converted directly to Lazy<IAnimal>, but since Dog can be converted to IAnimal you can use the Lazy<IAnimal> constructor overload that expects an IAnimal (strictly speaking, it takes a Func that returns an IAnimal) and provide a Dog instead:
public virtual Lazy<IAnimal> CreateLazyAnimal()
{
// default animal is a dog
return new Lazy<IAnimal>(() => new Dog());
}
Casting Lazy<Dog> to Lazy<IAnimal> is not allowed because the types are different (the Lazy<T> type inherits just from object). In some cases, the casting can make sense - for example casting IEnuerable<Dog> to IEnumerable<IAnimal>, but the casting isn't safe in all cases.
C# 4.0 adds support for this casting in the safe case. It is called covariance and contravariance. For example, this article gives a nice overview.
Unfortunatelly, in C# 4.0 this works only for interfaces and delegates and not for concrete classes (e.g. Lazy<T>). You could probably solve the problem by creating interface ILazy<out T> and a wrapper for standard Lazy type, but it is probably easier to just write conversion from Lazy<Dog> to Lazy<IAnimal>.
I was wondering if anyone could tell me if this kind of behaviour is possible in C# 4.0
I have an object hierarchy I'd like to keep strongly typed. Something like this
class ItemBase {}
class ItemType<T> where T : ItemBase
{
T Base { get; set; }
}
class EquipmentBase : ItemBase {}
class EquipmentType : ItemType<EquipmentBase> {}
What I want to be able to do to have something like this
ItemType item = new EquipmentType();
And I want item.Base to return type ItemBase. Basically I want to know if it's smart enough to strongly typed generic to a base class without the strong typing. Benefit of this being I can simply cast an ItemType back to an EquipmentType and get all the strongly typedness again.
I may be thinking about this all wrong...
You're talking about covariance which would allow you to do:
ItemType<object> item = new EquipmentType();
You couldn't do this in C# 4 because of the following reasons:
Generic covariance only works on interfaces, arrays, and delegate types, not base classes
Your ItemType class uses T as an in/out type parameter meaning it receives a T and also returns a T.
Number 2 is the main issue because if it were allowed, then the following code would have to be compilable, yet fail at runtime.
// this will not work
ItemType<object> item = new EquipmentType();
item.Base = new Object(); // this seems ok but clearly isn't allowed
Covariance and Contravariance FAQ
No, because ItemType as far as the compiler is concerned is a separate type from ItemType<EquipmentBase> or ItemType<Foo>. All three are treated as unique types and they cannot represent one another.
In your class declarations, you declared it as ItemType<T> and so ItemType would be an undefined type which would not compile.
At best, you could use an ItemType<EquipmentBase> object to represent EquipmentType or any other class derived from ItemType<EquipmentBase> but not ItemType<PersonType>.
I don't think that the new features of C# 4.0 will help you out there. However, there is a way around this which already works since generics were introduced: you create an abstract base class with the same name as the generic class and put all members which you want and which don't need to accept or return an argument of the generic type, like so:
class ItemBase {
}
abstract class ItemType {
public ItemBase Base {
get { return GetItemBase(); }
set { SetItemBase(value); }
}
protected abstract void SetItemBase(ItemBase value);
protected abstract ItemBase GetItemBase();
}
class ItemType<T> : ItemType where T : ItemBase {
protected override sealed void SetItemBase(ItemBase value) {
Base = (T) value;
}
protected override sealed ItemBase GetItemBase() {
return Base;
}
public new T Base { get; set; }
}