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.
Related
When I try define an variable like that:
IVeterinarian<IAnimal> v = (IVeterinarian<IAnimal>)new CatVeterinarian();
These are an example for the interfaces and classes decleration:
interface IAnimal
{
}
class Dog : IAnimal
{
}
class Cat : IAnimal
{
}
interface IVeterinarian<TAnimal> where TAnimal : IAnimal
{
void Heal(TAnimal animal);
}
class DogVeterinarian : IVeterinarian<Dog>
{
public void Heal(Dog animal)
{
}
}
class CatVeterinarian : IVeterinarian<Cat>
{
public void Heal(Cat animal)
{
}
}
What's the difference btw my example and declaring IEnumerable of strings in a IEnumerable of objects variable?
Why am I getting an InvalidCastException.
Any ideas?
You can't create an instance like that;
IVeterinarian v = (IVeterinarian)new CatVeterinarian();
Because, you should pass a type which is implemented from IAnimal;
IVeterinarian<Cat> v = new CatVeterinarian();
EDIT
You are getting cast invalid exception because IVeterinarian<IAnimal> is not IVeterinarian<Cat> even implements it.
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
As you see, IEnumerable uses out parameter and it makes it covariant. Please review this page.
In your case, you can't make your generic as covariant because the generic is being used as signature. By using covariants you can return derived types as you mentioned in the question.
IEnumerable<object> list = new List<string>();
In short, it has different purpose from your case which you want to try.
In my usecase, I wanted to take care differently each animal. I found an easy way to do it - Here's my solution:
Remove the generic from the IVeterinarian
Create a generic base Veterinarian class which implements IVeterinarian where T implements IAnimal
Create an abstract method in the base class with the same name that gets an animal argument in type T
Implement the original Heal method with calling the abstract method giving the animal argument after casting to T
In the Cat&Dog Veterinarian classes, just override the abstract method
Here's an example:
interface IAnimal
{
}
class Dog : IAnimal
{
}
class Cat : IAnimal
{
}
interface IVeterinarian
{
void Heal(IAnimal animal);
}
abstract class BaseVeterinarian<T> : IVeterinarian
where T : IAnimal
{
public void Heal(IAnimal animal)
{
Heal((T)animal);
}
protected abstract void Heal(T animal);
}
class DogVeterinarian : BaseVeterinarian<Dog>
{
protected override void Heal(Dog animal)
{
}
}
class CatVeterinarian : BaseVeterinarian<Cat>
{
protected override void Heal(Cat animal)
{
}
}
Be aware that you must send a correct IAnimal object as argument - or it will throw an InvalidCastException.
Now I can make IVeterinarian objects with different implementations almost like I whished in my question.
IVeterinarian v = new CatVeterinarian();
v.Heal(new Cat());
IVeterinarian v2 = new DogVeterinarian();
v2.Heal(new Dog());
Can something like this be accomplished using C#?
public abstract class BaseClass
{
public abstract IInterface<T> CreateEditor() where T: the_actual_type_of_this_instance;
}
Example usage:
var instance = new DerivedClass();
var editor = instance.CreateEditor(); // IInterface<DerivedClass>
No, you can't do that - partly because it wouldn't make sense at compile time. Consider a slight change to your code:
BaseClass instance = new DerivedClass();
var editor = instance.CreateEditor();
What could the compiler infer the type of editor to be? It only knows about BaseClass, so it would have to return an IInterface<BaseClass>. Depending on whether or not T in IInterface<T> is covariant, that could be valid for something which actually implemented IInterface<DerivedClass> or it might not be.
You might want to make your base class generic instead:
public abstract class BaseClass<T> where T : BaseClass<T>
{
public abstract IInterface<T> CreateEditor();
}
public class DerivedClass : BaseClass<DerivedClass>
{
...
}
There are two problems with this:
It doesn't work when you go more than one level deep; if you need subclasses of DerivedClass, you'll have issues
It doesn't prevent malicious abuse of the pattern, e.g. public class NastyClass : BaseClass<DerivedClass>
... but in many cases it's quite practical.
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
I'm facing a bit of problem with the following casting:
class A
{
}
class B : A
{
}
class C<T> where T : A
{
protected T property { get; set; }
}
class D : C<B>
{
}
class MainClass
{
public static void Main (string[] args)
{
C<A> x = new D();
// Error CS0029: Cannot implicitly convert type `SampleApp.D' to `SampleApp.C<SampleApp.A>' (CS0029) (SampleApp)
}
}
I don't understand why this is failing since D is wider than C<A> since it implements C<B>, and B : A. Any workarounds?
If you can use C# 4.0, you can write the following code.
class A { }
class B : A {}
interface IC<out T> {}
class C<T> :IC<T> where T : A { protected T property { get; set; } }
class D : C<B> {}
class MainClass {
public static void Main()
{
IC<A> x = new D();
}
}
Let's name your classes Animal for A, Barker for B, and Dog for D.
Actually C<Animal> is wider than Dog : C<Barker>. Assume you have public property Me of type T and assignment possible:
C<Animal> a = new Dog();
a.Me = Elephant; // where Elephant inherited from Animal
Oops! Dog is parametrized with Barker. Have you seen barking elephants?
You need to declare some covariant interface to allow assignment of class instantiated with more derived type argument C<Barker> to object instantiated with less derived type argument C<Animal>. You can use empty interface, like #NickW suggested, but you will not be able to do something with instance of that interface (it's empty!). So, let's do something like that:
interface IC<out T>
where T : Animal
{
IEnumerable<T> Parents(); // IEnumerable is covariant
T Me { get; } // no setter
}
class C<T> : IC<T>
where T: Animal
{
// implementation
}
class D : C<Barker>
{
// implementation
}
Above scenario is still impossible, but now you can
IC<Animal> a = new Dog();
foreach(var parent in a.Parents)
Console.WriteLine(parent);
Console.WriteLine(a.Me);
You can't do that because the Generics are actualy templates and they don't act like what you want to do with them. Let me show you by this:
When you say "C<A>" it means a generic class by a "parameter" of "A".
BUT
When you say "D" it means exactly "D"!
So D is not equal to a generic class by a parameter of A. As you can simply see it in the result of ToString function on both types (by using typeof).
Hope it helps
Cheers
I have some generic interfaces and classes that implement those intefaces like so:
interface A<M, N>
where M : X<N>
where N : Y
{
}
class B<M, N> : A<M, N>
where M : X<N>
where N : Y
{
}
interface X<M> where M : Y
{
}
interface Y
{
}
class X1<M> : X<M> where M : Y
{
}
class Y1 : Y
{
}
I know it seems like a very messy way of doing things, but I sort of need it for my application. My question is how come I can't do this:
A<X<Y>, Y> variable = new B<X1<Y1>, Y1>();
Marc is right; just to give you some more background on why this cannot work. Consider the following re-naming of your code:
interface IZoo<TCage, TAnimal>
where TCage : ICage<TAnimal>
where TAnimal : IAnimal
{
}
class Zoo<TCage, TAnimal> : IZoo<TCage, TAnimal>
where TCage : ICage<TAnimal>
where TAnimal : IAnimal
{
}
interface ICage<TAnimal> where TAnimal : IAnimal
{
}
interface IAnimal
{
}
class FishTank<TAnimal> : ICage<TAnimal> where TAnimal : IAnimal
{
}
class Fish : IAnimal
{
}
And now your question is, why is this not legal:
Zoo<FishTank<Fish>, Fish> aquarium = new Zoo<FishTank<Fish>, Fish>();
IZoo<ICage<IAnimal>, IAnimal> zoo = aquarium;
?
Because suppose now there is a method on IZoo:
interface IZoo<TCage, TAnimal>
where TCage : ICage<TAnimal>
where TAnimal : IAnimal
{
void PutAnimalInCage(TCage cage, TAnimal animal);
}
And you then say:
zoo.PutAnimalInCage(giraffePaddock, giraffe);
And you just put a giraffe paddock into an aquarium! We cannot maintain type safety in a world where the conversion you want is legal and IZoo can have any method you choose on it.
Now, this is only dangerous because IZoo has such a method. If it doesn't have such a method then you are right, that could be perfectly safe. In C# 4.0 we added a feature to the language so that you can ask the compiler "check whether this interface can be made safely variant", by annotating the type parameters you want to be covariant with "out", and the ones you want to be contravariant with "in". If you do that then the compiler will check for you whether the variance you want can be made to be typesafe. If it cannot, then it will not allow the declaration of the type.
The way this question usually comes up on StackOverflow is people asking why is this illegal:
List<Giraffe> giraffes = new List<Giraffe>();
List<Mammal> mammals = giraffes; // illegal
Same reason. Because then nothing stops you from later
mammals.Add(new Tiger());
and you've just added a tiger to a list of giraffes. Same reasoning, just a much simpler case.
Variance needs to be explicit (and requires C# 4.0); for example, this makes it compile as covariant (note the out modifiers in the interface):
interface A<out M, out N>
where M : X<N>
where N : Y
{
}
interface X<out M> where M : Y
{
}
Note, however, that this also limits your interface to ... covariance! You couldn't have an Add(M) method, for example - as that would need to be contavariant (aka in).