0

Suppose I have following code:

public class CBase: AbstractC,IRenderable
{
 //code here
}

public class CBaseGroup
{
 private List<IRenderable> CCollection;

 public CBaseGroup(List<IRenderable> c)
 {
   CCollection=c;
 }
}

public class CGroup:CBaseGroup
{
 public CGroup(List<CBase> c):base(c) 
 //here fails because cannot convert List<CBase> to List<IRenderable>
 {
 }

}

Why it does not compile?

Please feel free to suggest a nappropriate title.

From Matthew Scharley answer I found that the code should look like:

  public class CGroup:CBaseGroup
    {
     public CGroup(List<CBase> c):base(c.Cast<IRenderable>().ToList()) 
      // cast to list since I'm not using IEnumerable
     {
     }

    }

This at least satisfies compiler.

3
  • 2
    Covariance and contravariance. Is there one good answer we can always point back to? Commented Aug 18, 2009 at 22:23
  • Yes, that is the only/best way to do it in C# 3.5. Hopefully we get a better solution with C# 4.0 (I havn't looked too closely at it, just what I hear around here). Commented Aug 18, 2009 at 22:43
  • No, we won't get a better solution in C# 4.0, nor in any other language which is typesafe. See stackoverflow.com/questions/981570/… Commented Aug 18, 2009 at 23:44

4 Answers 4

4

To get the effect you want you'd need to do something like

public class CBaseGroup<T> where T : IRenderable
{
 private List<T> CCollection;

 public CBaseGroup(List<T> c)
 {
   CCollection=c;
 }
}
public class CGroup:CBaseGroup<CBase>
{
 public CGroup(List<CBase> c):base(c) 
 {
 }
}

to inject the specific type of IRenderable into the base class.

Sign up to request clarification or add additional context in comments.

Comments

2

C# 3.5 doesn't support contravarience and covarience. I believe I've heard that C# 4.0 will (someone correct me if I'm wrong.)

For a more extended discussion on this, have a look at this other question.

3 Comments

Yes C# 4.0 does include support for contra/covariance
Since List<T> is inherently invariant, variance support in the language won't help you one bit here. It's just not typesafe to do that, period.
There are many cases where casting a List<Super> to a List<Sub> (or perhaps List<object>!) would be incredibly useful. I'd argue that you should be able to, since if you created a List<object> in the first place, you've still got the same issues anyway, there's no new problems introduced by making the List<T> class variant.
0

Well, on line 1, you need to include the parent class before any interfaces

public class CBase: AbstractC, IRenderable

1 Comment

Pretty sure order makes no difference here. Stylistically, you're probably right, but interfaces (should) always start with 'I', so it really shouldn't be too much of an issue.
0

This is a covariance problem as C# 3.5 does not support this kind of generic type conversion.There will be more support for generic co- and contra-variance in C# 4.0.

The below type of conversion works as we expect and have done many times

IRenderable renderable = cbaseObject as IRenderable;

A more specific type, cbaseObject, is being converted to a more general type, IRenderable, in the class hierarchy. However, the following which is similar to your case

List<CBase> listOfCBaseObjects = new List<CBase>();
IEnumerable<IRenderable> renderables = listOfCBaseObject as IEnumerable<IRenderable>;

will not work because it is not supported in current C# releases.

However, support for covariance of arrays that hold reference types has been there since C# 1.0. See Eric Lippert's blog port. So if you do the following, the conversion will work:

CBase [] cbaseObjects = new CBase[] { new CBase(),new CBase()};
IRenderable [] renderables = cbaseObjects as IRenderables[];

So alternatively, if you want, you can change CBase and CBaseGroup constructors to take an array instead of a list. So that you can do the following:

//constructor
public CBaseGroup(IRenderable[] c)

//constructor
public CGroup(CBase[] c):base(c as IRenderable[]) 

and this should work.

2 Comments

This is because it's not really covarience at all, it's a simple cast of each element of the array. This is something the compiler can handle, as opposed to a generic parameter which can be used for anything.
I believe it is covariance at least by definition. The conversion operation between two arrays is allowed such that what you end up with still preserves the ordering of types contained. See the array covariance section in the C# language spec msdn.microsoft.com/en-us/library/aa664572(VS.71).aspx. It may be implemented as you described but that does not make it not real covariant.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.