Covariance issue - c#

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

Related

Convert Derived Class to Base Generic Class

I have a set up approximately like this:
public class A { ... }
public class A<T> : A { ... }
public class B<T> : A<T> { ... }
public class C : B<SomeType> { ... }
I am iterating over a list of type A, and I want to check if something is of type B, and if it is, convert it to... B... I can see how it already poses a problem of how am I gonna get the type... but if I cannot do that, what are my other alternatives?
EDIT:
To clarify, B contains a method I'd like to use that A does not contain as it simply has no need for it... I want to check which of the objects on that list are of type B so that I can run this method on them
EDIT 2:
I forgot to mention the A in the middle earlier... sorry for the confusion
I want to check which of the objects on that list are of type B
Then introduce a type B into your type hierarchy. Currently you don't have a type B, you've got B<SomeType> which is a different type. so
public class A { ... }
public abstract class B : A { ... }
public class B<T> : B { ... }
public class C : B<SomeType> { ... }
Or declare the method you want on an interface
public class A { ... }
public class A<T> : A { ... }
public interface IB
public class B<T> : A<T>, IB { ... }
public class C : B<SomeType> { ... }
Then for any A you can check if it is IB. This is probably the most natural solution since .NET doesn't have multiple inheritance.

C# Casting Generic Child Type to Parent

Let's say we have these types:
class A {}
class B : A {}
class X<T> {}
Why we can't do this?
X<A> var = new X<B>();
Is there any workaround available?
[Edit]
I tried to use covariance but it failed because I want to access a property inside X that is of type T and C# does not allow using type T in the interface:
interface IX<out T> {
T sth {set; get;}
}
class X<T>: IX<T> {
T sth { set; get; }
}
[Edit 2]
I also tried this but it failed:
class X<T> where T : A
{
public T sth { set; get; }
public static implicit operator X<T>(X<B> v)
{
return new X<T>
{
sth = v.sth,
};
}
}
It's strange that C# does not allow to the casting for 'sth'.
The problem is that classes does not support Covariance and Contravariance, only interfaces:
class A { }
class B : A { }
class X<T> : P<T> { }
interface P<out T>
{
}
...
P<A> var = new X<B>();
Covariance and Contravariance FAQ
You need covariance (mark the type parameter T with the word out):
interface IX<out T> {}
This is only allowed with interface types (and delegate types). And the type B must be a reference type (class like here is OK).
Then this is fine:
IX<B> ixb = ...;
IX<A> ok = new IX<B>();

Cast instance of generic type to "template" instance

This might be a silly question and I don't really need this for anything but I was just curious...
The best way to describe it is using a example so here it is:
using System;
namespace GenericExample
{
public interface IFoo { }
public interface IFoo2 { }
public class Foo1: IFoo , IFoo2 { }
public class Foo2 : IFoo, IFoo2 { }
public class MyGeneric<T> where T : IFoo , IFoo2, new() { }
internal class Program
{
public static void Main(string[] args)
{
MyGeneric<Foo1> obj1 = new MyGeneric<Foo1>();
MyMethod(obj1);//I can treat obj1 as MyGeneric<T> in MyMethod
MyGeneric<Foo2> obj2 = new MyGeneric<Foo2>();
//But can I use is as MyGeneric<T> in this method???
//MyGeneric<?> obj3 = null;
//obj3 = (MyGeneric<?>)obj1;
//obj3 = (MyGeneric<?>)obj2;
Console.ReadLine();
}
public static void MyMethod<T>(MyGeneric<T> arg) where T : IFoo, IFoo2, new()
{
}
}
}
I don't think it is possible to treat obj1 as MyGeneric< T> in Main
but at the same time it feels strange since I can pass it as a MyGeneric< T> argument
You cannot cast it to MyGeneric<T> in Main because in the scope of Main there is no such type as T. Actually it's not really clear what you mean by
to treat obj1 as MyGeneric< T> in Main
When passing obj1 to MyMethod you don't "treat it as MyGeneric<T>". It is the compiler which infers the type of T for you. It knows that T is Foo1 here and translates your call
MyMethod(obj1);
to
MyMethod<Foo1>(obj1);
So the type of the parameter arg inside of MyMethod will at runtime also be MyObject<Foo1>, not an unspecified MyObject<T>.
There is no common base-type for MyGeneric and MyGeneric, so I assume the answer is no. In contrast to Java generics in C# are strongly typed types and not just placeholders, so they donĀ“t have anything in common - except a name. However actually they are different types, think of them as just MyGeneric<T1> being a type Foo and MyGeneric<T2> being Bar.
A way around this is to define a non-generic version of your generic class:
public class Foo1 { }
public class MyNonGeneric { }
public class MyGeneric<T> : MyNonGeneric where T : new() { }

c# implement interface method with parameter of subclass type

I have this desired class hierarchy:
interface IClass
{
string print(IClass item);
}
class MyClass : IClass
{
// invalid interface implementation
// parameter type should be IClass not MyClass
string print(MyClass item)
{ return item.ToString(); }
}
I tried to solve interface implementation problem by using generic types as next with no success:
interface IClass
{
string print<T>(T item) where T : IClass;
}
class MyClass : IClass
{
string print<T>(T item) where T : MyClass
{ return item.ToString(); }
}
What should I do?
Make your interface generic
interface IClass<T> where T : IClass<T>
{
string print(T item);
}
class MyClass : IClass<MyClass>
{
public string print(MyClass item)
{
return item.ToString();
}
}
It's helpful to understand why this is illegal. The feature you want is formal parameter type covariance, and very few languages offer it. (Eiffel, I think has this as a feature.) It is not often found in languages because it is not safe! Let me illustrate with an example:
class Animal {}
class Lion : Animal { public void Roar() { } }
class Giraffe : Animal { }
interface IFoo { void M(Animal a); }
class C : IFoo
{
public void M(Lion lion) { lion.Roar(); }
}
class P
{
public static void Main()
{
IFoo foo = new C();
foo.M(new Giraffe());
}
}
And we just made a giraffe roar.
If you look at all those type conversions, the only one that can sensibly be made illegal is matching C.M(Giraffe) to IFoo.M(Animal).
Now, formal parameter type contravariance is typesafe but it is not legal in C# except in some very limited circumstances. If C# supported it, which it does not, then you could safely do something like this:
interface IBar { void M(Giraffe g); }
class D : IBar
{
public void M(Animal animal) { ... }
}
class P
{
public static void Main()
{
IBar bar = new D();
bar.M(new Giraffe());
}
}
See what happened there? IFoo.M says "I can take a giraffe", and C.M says "I can accept any giraffe because in fact I can accept any animal". That would be typesafe if C# supported it, but it only supports it in two ways:
Contravariant generic delegate and interface conversions.
Contravariant method group conversions to delegate types.
An example of the first is that an expression of type IComparable<Animal> may be assigned to a variable of type IComparable<Giraffe> by the same logic: a method that compares two animals can be used where a method that compares two giraffes is needed. This was added in C# 4.
An example of the second is:
delegate void MyFunction(Giraffe g);
...
D d = new D();
MyFunction myfunc = d.M;
Again, we need a function that takes a Giraffe, and we supply one that takes any Animal. This feature was added in C# 2.
You just need to pass IClass as parameter to your method.
interface IClass
{
string print(IClass item);
}
class MyClass : IClass
{
public string print(IClass item)
{ return item.ToString(); }
}
Here's A solution that will get you compiling at least:
interface IClass
{
string print<T>(T item) where T : IClass;
}
class MyClass : IClass
{
string print(IClass item) => item is MyClass i ? i.ToString() : string.Empty;
}

Implementing nested generic Interfaces

I have the following Classes / Interfaces:
// Model
public class A : IA { }
// ModelLogic
public class B : IB<A> { }
// Model Interface
public interface IA { }
// ModelLogic Interface
public interface IB<T> where T : IA { }
I try to create a new instance using the following code:
IB<IA> foo = new B();
I am getting the following error:
Cannot implicitly convert type 'B' to 'IB<IA>'. An explicit conversion exists (are you missing a cast?)
Can someone please explain why this is not possible?
OK, let's replace A with Fish, IA with IAnimal, B with Aquarium, and IB<T> with IContainer<T>. And we'll add a member to IContainer<T>, and a second implementation of IAnimal:
// Model
public class Fish : IAnimal { }
public class Tiger : IAnimal { }
// ModelLogic
public class Aquarium : IContainer<Fish>
{
public Fish Contents { get; set; }
}
// Model Interface
public interface IAnimal { }
// ModelLogic Interface
public interface IContainer<T> where T : IAnimal
{
T Contents { get; set; }
}
IContainer<IAnimal> foo = new Aquarium(); // Why is this illegal?
foo.Contents = new Tiger(); // Because this is legal!
You can put a Tiger into foo -- foo is typed as a container that can contain any animal. But you can only put a Fish into an Aquarium. Since the operations you can legally perform on an Aquarium are different than the operations you can perform on an IContainer<IAnimal>, the types are not compatible.
The feature you want is called generic interface covariance and it is supported by C# 4, but you have to prove to the compiler that you will never put a tiger into your fish tank. What you want to do is:
// Model
public class A : IA { }
// ModelLogic
public class B : IB<A> { }
// Model Interface
public interface IA { }
// ModelLogic Interface
public interface IB<out T> where T : IA { }
Notice the covariance annotation on IB. This out means that T can only be used as an output, not as an input. If T is only an output then there is no way for someone to put a tiger into that fish tank because there is no "put into" property or method possible.
I wrote a number of blog articles while we were adding that feature to C#; if you are interested in the design considerations that went into the feature, see:
http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/
To fix your code, just change
public interface IB<T> where T : IA { }
to
public interface IB<out T> where T : IA { }
It's not easy to see when you have empty interfaces. Consider you have one method M in interface IB:
public interface IB<T> where T : IA
{
void M(T t);
}
And here is implementation of B:
public class B : IB<A>
{
public void M(A t)
{
// only object of type A accepted
}
}
Then you have object C, which also implements IA:
public class C : IA { }
So, if your code would be possible, then you could call:
IB<IA> foo = new B();
foo.M(new C());
Problem is that class B accepts only objects of type A. Error!

Categories

Resources