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).
Related
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.
public interface ISomeInterface
{
IOut SomeMethod(IIn aIn)
}
public class MyOut : IOut
{
public string AnExtraProp {get; set;}
}
public class MyIn : IIn
{
public string AnotherExtraProp {get; set;} }
}
public class MyConcreteOfSomeInterface : ISomeInterface
{
public MyOut SomeMethod(MyIn aIn)
{
}
}
Is it possible to have many classes (eg. MyConcreteOfSomeInterface, MyConcrete2OfSomeInterface, ....) implement an interface (eg. ISomeInterface) but yet have parameters of a concrete type (eg. MyIn, MyOut etc.).
I realise I could declare:
public interface ISomeInterface<TIn, TOut>
{
TOut SomeMethod(TIn aIn)
}
but as ISomeInterface will have many methods this will not be practical. So say I need to add additional methods SomeMethod2 and SomeMethod3 then I would end up with:
public interface ISomeInterface<TIn, TOut, TIn2, TOut2, TIn3, TOut3>
{
TOut SomeMethod(TIn aIn)
TOut2 SomeMethod(TIn2 aIn)
TOut3 SomeMethod(TIn3 aIn)
}
so the declaration becomes unwieldy pretty quickly.
What design pattern can I use to achieve:
Many concrete classes implementing an interface ISomeInterface AND
Using concrete parameters/return values that are implementing the necessary interfaces IIn, IOut?
There will be many methods on ISomeInteface with different types for the parameter/interface combos.
Let's simplify the problem. Suppose we have:
class Animal {}
class Giraffe : Animal {}
interface IFoo
{
Animal M();
}
Can we then have
class C : IFoo
{
public Giraffe M() => new Giraffe();
}
Unfortunately no. An interface implementation must match exactly.
Now, you might think "hey, the interface demands that an animal be returned, and I am returning an animal, namely, a giraffe, so what's the problem?"
The answer is that there is no problem. C# could have a type system where this works, and this feature has been proposed many many many times. It's called "return type covariance", and if you do a search here you'll find many questions about it.
However C# does NOT have this feature, and so you're out of luck. The best you can do is:
class C : IFoo
{
Animal IFoo.M() => this.M();
public Giraffe M() => new Giraffe();
}
And now you're good. The IFoo contract is explicitly implemented, and the public surface of the class has the more specific signature.
Similarly, what if we had:
interface IBar()
{
void N(Giraffe g);
}
This is not legal:
class D : IBar
{
public void N(Animal g) { ... }
}
Again, this would be perfectly sensible. IBar requires that D.N be a thing you can pass a giraffe to, and D.N is a thing that you can pass a giraffe or any animal to. But again, C# does not support this feature. This is called formal parameter contravariance and a very small number of programming languages support it.
Do a search on C# covariance and contravariance for details on what kinds of variance are supported by C#.
Also, note that this would not be typesafe:
interface IBaz
{
void P(Animal a);
}
class E : IBaz
{
public void P(Giraffe g) { }
}
Because you need to be able to say ((IBaz)(new E())).P(new Tiger()). IBaz says that an implementation must be able to accept any animal, so you cannot implement it with a method that only accepts giraffes. Logically it would be safe for return types to get more specific, but formal parameter types have to get less specific. That's why it's return type covariance but formal parameter type contravariance, because the direction of convertibility changes in the contra case.
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());
Let's say I have a set of classes/interfaces:
class ObjectData { }
class UnitData : ObjectData { }
class Component1Data : UnitData { }
class Component2Data : UnitData { }
interface IObject { }
interface IUnit : IObject { }
interface IComponent1 : IUnit { }
interface IComponent2 : IUnit { }
abstract class Object<D, O, I>
where D : ObjectData
where O : Object<D, O, I>, I, new()
where I : IObject
{ }
The point of primary interest here is an Object class which is a base generic class in some hierarchy. Type-param "O" is a way to specify type of an actual class which is derived from the Object. Thus, something like this can be declared and compiled w/o problems:
class Unit : Object<UnitData, Unit, IUnit>, IUnit { }
But what i want to do, is to define another generic "2nd-level" class derived from Object that should also behave as a base class for a couple of similar "3rd-level" entities. And it have to be non-abstract because it is also some sort of an entity. So i need to define something like this:
class Unit<D, I> : Object<D, Unit<D, I>, I>, IUnit
where D : UnitData
where I : IUnit
{ }
class Component1 : Unit<Component1Data, IComponent1>, IComponent1 { }
class Component2 : Unit<Component2Data, IComponent2>, IComponent2 { }
And it produces following compilation error:
error CS0311: The type 'Unit<D, I>' cannot be used as type parameter 'O' in the generic type or method 'Object<D, O, I>'. There is no implicit reference conversion from 'Unit<D, I>' to 'I'.
The question is why? In my vision if Unit<D, I> is implementing IUnit, and param "I" is specified as where I : IUnit, then all should be fine. That's how i see it. What i don't see?
Simplify the problem;
interface IObject { }
interface IUnit : IObject { }
interface IFoo : IUnit { }
abstract class Object<O, I>
where O : Object<O, I>, I, new()
where I : IObject
{}
class Unit : Object<Unit, IUnit>, IUnit
{
}
This is happy and compiles. I've replaced I here with IUnit. Now change to something more derived:
class Unit : Object<Unit, IFoo>, IUnit
and you get the error:
The type 'Unit' cannot be used as type parameter 'O' in the generic
type or method 'Object'. There is no implicit reference
conversion from 'Unit' to 'IFoo'.
So... Unit, which derives from IUnit cannot be converted to IFoo even though both implement IUnit... because Unit doesn't derive from IFoo... which is a condition on Object:
where O : Object<O, I>, I`
which requires you to do something you're not allowed to do :
class Unit<I> : Object<Unit<I>, I>, I
Like others have commented, your generics are too complex.
At least, it seems to me, that there's no need for I type parameter, because your Objects will implement corresponding interface.
So, the code could be simplified like this:
abstract class Object<D, O> : IObject
where D : ObjectData
where O : Object<D, O>
{
}
class Unit<D> : Object<D, Unit<D>>, IUnit
where D : UnitData
{
}
Without full understanding, how O will be used inside of Objects hierarchy, it is hard to say, is it possible to throw away O type parameter.
You've mentioned a static factory method - this is definitely not a reason to bring such complexity. But, of course, you know more about use cases.
I have type hierarchy defined like this:
interface IMyClass
{
}
interface IBase1<T>
{
}
interface IBase2<T>
{
}
interface IMyDerived1 : IBase1<IMyClass>
{
}
class Base1<T, U> : IBase1<T>
where U : IBase2<T>
{
}
class Base2<T, U> : IBase2<T>
where U : IBase1<T>
{
}
class Derived1<T, U> : Base1<T, U>, IMyDerived1
where T : IMyClass
where U : IBase2<T>
{
}
class Derived2<T, U> : Base2<T, U*>
where T : IMyClass
where U : IMyDerived1
{
}
but Visual Studio 2008 (.net 3.5 SP1) says that parameter U in parent specifier of Derived2 (marked with *) is not convertible to IBase1<T>. Is this solvable?
EDIT:
It indeed looks like generics overuse but allows Base1,2 and Derived1,2 to apply operations on supplied types without a casts. Something like this:
class MyClass : IMyClass
{}
class MySpecific1 : Derived1<MyClass, MySpecific2>
{
// use inherited properties and methods of type MyClass here
// use properties of MySpecific2 returning MyClass without casts
}
class MySpecific2 : Derived2<MyClass, MySpecific1>
{
// use inherited properties and methods of type MyClass here
// use properties of MySpecific1 returning MyClass without casts
}
Probably this can be solved more elegantly with variance in .net4 but I'm stuck with 3.5 for now.
class Derived2<T, U>: Base2<T, U>
where T: IMyClass
where U: IMyDerived1, IBase1<T>
{
}
That hurt my head!
Having a look at it, the problem lies with this definition:
interface IMyDerived1 : IBase1<IMyClass>
{
}
You've specialised that generic implementation, and then attempted to use with generic arguments later on:
class Derived2<T, U> : Base2<T, U>
where T : IMyClass
where U : IMyDerived1
{
}
Which is invalid. Not sure if this is correct, but can you make either this change:
interface IMyDerived1<T> : IBase1<T>
{
}
class Derived2<T, U> : Base2<T, U>
where T : IMyClass
where U : IMyDerived1<T>
{
}
That's a complicated hierarchy you're designing there, what will be its use?
The problem is, in Derived2, T is not IMyClass, it could be some other class implementing this interface. In Base1, it is specified to be exactly IMyClass. Types with different generic arguments are not compatible in C# 3.0.
For me, this looks a bit like generic overuse. But I can't see the context.