Inheritance Question

G

Gary Rynearson

The code below does not compile and I cannot figure out why. The compiler
issues the error: "Cannot convert type 'CustomCollection' to
System.Collections.Generic.List<IGraphableRecord>".

1.
I have a IGraphableRecord interface. The class CustomGraphRecord implements
the interface (so, CustomGraphRecord "is a" IGraphableRecord).

2.
I have a CustomCollection class which is a generic list of CustomGraphRecord
objects.

3.
OK, now I have an interface, IGraphableTable, which specifies a method
GetGraphData () that returns a Generic list of IGraphableRecord objects. I
also have a class which attempts to implement the interface. It contains a
private variable (_collectionOfRecords) of type, CustomCollection. I am
trying to satisfy the interface requirement of implementing the
GetGraphData() method by returning the variable _collectionOfRecords.
However, this is where the above mentioned compile error occurs.

I have tried a couple variations, but have been uncessfull at getting a
compile. Can anyone provide ideas on what is wrong and how to fix it?

Gary



using System;

using System.Collections.Generic;

using System.Text;

public interface IGraphableRecord

{

int X { get;}

int Y { get;}

}

public class CustomGraphRecord : IGraphableRecord

{

int _x, _y;

public int X { get { return _x; } }

public int Y { get { return _y; } }

public string Name { get { return "Custom"; } }

}

public class CustomCollection : List<CustomGraphRecord>

{

}

public interface IGraphableTable

{

List<IGraphableRecord> GetGraphData();

}

public class CustomGraphUnit : IGraphableTable

{

CustomCollection _collectionOfRecords = new CustomCollection();

public List<IGraphableRecord> GetGraphData()

{

return (List<IGraphableRecord>) _collectionOfRecords;

}

}
 
P

pudchuck

This had me stumped as well. I researched it a bit and encountered a
few discussions about generics not being covariant.

See http://www.fotia.co.uk/fotia/DN.01.CoVariantGenericList.aspx and
http://msdn.microsoft.com/en-gb/library/aa479859.aspx#fundamentals_topic12

This example from the first link sums it up rather nicely.

public class Adult : IPerson
{
//
}

public class Child : IPerson
{
//
}

public void WhyCoVarianceIsNotAllowed()
{
List<Adult> adults = new List<Adult>();
adults.Add(new Adult());
// If the following line would be allowed...
List<IPerson> people = adults;
// This line would have to be legal, i.e. adding a Child to an
Adult List.
people.Add(new Child());
}

The first link tells you how to work around this problem as well.

Cheers,
Bill
 
G

Gary Rynearson

pudchuck

Thanks for the information ... it is exactly what I needed to know.

Gary
 
G

Gary Rynearson

OK ... that link does explain why the code does not compile, and I am now
able to get the code to compile. However, my NUnit test generates a runtime
exception:

TestCovariantGenerics.TestCreatingObjects.A01_CanCreateCustomGraphUnit :

System.InvalidCastException :

Unable to cast object of type

'CovariantGenerics.EnumerableGeneric`2[CovariantGenerics.CustomGraphRecord,

CovariantGenerics.IGraphableRecord]'

to type

'System.Collections.Generic.IList`1[CovariantGenerics.IGraphableRecord]'.



I think the code gets pretty ugly, in terms of casting requirements, in the
GetGraphData() method of the CustomGraphUnit class.

Can anyone provide suggestions on how to get this test to work?

Here is the new code:
using System;

using System.Collections.Generic;

using System.Text;

using System.Collections;

namespace CovariantGenerics

{

public interface IGraphableRecord

{

int X { get;}

int Y { get;}

}

public class CustomGraphRecord : IGraphableRecord

{

int _x, _y;

string _name;

public int X { get { return _x; } }

public int Y { get { return _y; } }

public string Name { get { return _name; } }

public CustomGraphRecord(int x, int y, string name)

{

_x = x;

_y = y;

_name = name;

}

}

public class CustomCollection : List<CustomGraphRecord>

{

}

public interface IGraphableTable

{

IList<IGraphableRecord> GetGraphData();

}

public class EnumerableGeneric<TClass, TInterface> : IEnumerable<TInterface>

where TClass : TInterface

{

private IList<TClass> list;

public EnumerableGeneric(IList<TClass> list)

{

this.list = list;

}

public IEnumerator<TInterface> GetEnumerator()

{

foreach (TClass item in list)

yield return item;

}

IEnumerator IEnumerable.GetEnumerator()

{

return this.GetEnumerator();

}

}

public class CustomGraphUnit : IGraphableTable

{

CustomCollection _collectionOfRecords;

public CustomGraphUnit(CustomCollection coll)

{

_collectionOfRecords = coll;

}

public IList<IGraphableRecord> GetGraphData()

{

return (IList<IGraphableRecord>)

new EnumerableGeneric<CustomGraphRecord, IGraphableRecord>

(_collectionOfRecords); // _collectionOfRecords;

}

}

}

namespace TestCovariantGenerics

{

using CovariantGenerics;

using NUnit.Framework;

[TestFixture]

public class TestCreatingObjects

{

[Test]

public void A01_CanCreateCustomGraphUnit()

{

// Create CustomCollection

CustomCollection coll = createCustomColl();

CustomGraphUnit cgu = new CustomGraphUnit(coll);

IList<IGraphableRecord> gRecs = cgu.GetGraphData();

}

private CustomCollection createCustomColl()

{

CustomCollection coll = new CustomCollection();

for (int i = 0; i < 10; i++)

{

CustomGraphRecord rec = new CustomGraphRecord(

i + 1,

(i + 3) * 2,

"item " + i.ToString());

coll.Add(rec);

}

return coll;

}

}

}
 
G

Gary Rynearson

OK, here is a working solution, with NUnit tests. My model uses point and
line objects.

using System;

using System.Collections.Generic;

using System.Text;

using System.Collections;

namespace CovariantGenerics

{

public interface IGraphablePoint

{

int X { get;}

int Y { get;}

}

public class PointWithLable : IGraphablePoint

{

int _x, _y;

string _name;

public int X

{

get { return _x; }

set { _x = value; }

}

public int Y

{

get { return _y; }

set { _y = value; }

}

public string Name

{

get { return _name; }

set { _name = value; }

}

public PointWithLable(int x, int y, string name)

{

_x = x;

_y = y;

_name = name;

}

public override string ToString()

{

return string.Format("X = {0}; Y = {1}; Name = {2}",

X, Y, Name);

}

}

public class PointWithNoLable : IGraphablePoint

{

int _x, _y;

#region IGraphablePoint Members

public int X

{

get { return _x; }

set { _x = value; }

}

public int Y

{

get { return _y; }

set { _y = value; }

}

#endregion

public PointWithNoLable(int x, int y)

{

_x = x;

_y = y;

}

public override string ToString()

{

return string.Format("X = {0}; Y = {1}",

X, Y);

}

}

public class LabledPointList : List<PointWithLable>

{

}

public class UnLabledPointList : List<PointWithNoLable>

{

}

public interface IGraphableLine

{

IEnumerable<IGraphablePoint> GetThePoints();

}

public class LabledLine : IGraphableLine

{

LabledPointList _points;

public LabledLine(LabledPointList points)

{

_points = points;

}

#region IGraphableLine Members

public IEnumerable<IGraphablePoint> GetThePoints()

{

return new EnumerableGeneric<PointWithLable, IGraphablePoint>(_points);

}

#endregion

}

public class UnLabledLine : IGraphableLine

{

UnLabledPointList _points;

public UnLabledLine(UnLabledPointList points)

{

_points = points;

}

#region IGraphableLine Members

public IEnumerable<IGraphablePoint> GetThePoints()

{

return new EnumerableGeneric<PointWithNoLable, IGraphablePoint>(_points);

}

#endregion

}

public class EnumerableGeneric<TClass, TInterface> : IEnumerable<TInterface>

where TClass : TInterface

{

private List<TClass> list;

public EnumerableGeneric(List<TClass> list)

{

this.list = list;

}

public IEnumerator<TInterface> GetEnumerator()

{

foreach (TClass item in list)

yield return item;

}

IEnumerator IEnumerable.GetEnumerator()

{

return this.GetEnumerator();

}

}

}

namespace TestCovariantGenerics2

{

using CovariantGenerics;

using NUnit.Framework;

[TestFixture]

public class TestCreatingObjects

{

[Test]

public void A01_CanCreateLineWithNamePoints()

{

// Create CustomCollection

LabledPointList pList = createLabledPointList();

LabledLine line = new LabledLine(pList);

foreach (PointWithLable pt in line.GetThePoints())

{

//Console.WriteLine("Point is type: " +

// pt.GetType().ToString());

Console.WriteLine(pt.ToString());

}

}

[Test]

public void A01_CanCreateLineWithNoNamePoints()

{

UnLabledPointList pList = createUnLabledPointList();

UnLabledLine line = new UnLabledLine(pList);

foreach (PointWithNoLable pt in line.GetThePoints())

{

Console.WriteLine(pt.ToString());

}

}

private UnLabledPointList createUnLabledPointList()

{

UnLabledPointList upList = new UnLabledPointList();

for (int i = 0; i < 10; i++)

{

upList.Add(new PointWithNoLable(

(i + 1),

(i + 2) * 3));

}

return upList;

}



private LabledPointList createLabledPointList()

{

LabledPointList p = new LabledPointList();

for (int i = 0; i < 10; i++)

{

p.Add(new PointWithLable(

(i + 1),

(i + 2) * 3,

"Name " + i.ToString()));

}

return p;

}

}

}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top