views:

250

answers:

2

Hi,

I have a list of base objects (RTUDevice) and want to iterate through and convert each to a derived object (actually a derived of a derived RTDSensor) , however the downcasting is throwing an error.

public RTUDevice(int id)
{
    _id = id;
}

public class RTDDevice : RTUDevice
{
    public RTDDevice(int id)
        : base(id)
    {

    }
}

public class RTDSensor : RTDDevice
{
    public RTDSensor(int id)
        : base(id)
    {

    }
}

RTDSensor return = (RTDSensor)_devices.Find(d => d.Id == p.ReturnId);

Would it be better to pass the base object in a constructor to RTDSensor like

public RTDSensor(RTUDevice rtu) : base(rtu.Id)
{
}

or is my OOP design way off the mark.

A: 

I don't think this really has anything to do with the constructor call - the problem is you're trying to cast an object of one type to another. That never calls a constructor, unless you've got a user-defined conversion involved (which you can't if the two types involved are in the same inheritance hierarchy.)

If you want to create a new object of the more derived type, go ahead:

RTUDevice device = _devices.Find(d => d.Id == p.ReturnId);
RTDSensor sensor = new RTDSensor(device); // Or whatever

If you want to convert all the objects, and this is a List<RTUDevice> then you could use:

List<RTDSensor> sensors = _devices.ConvertAll(device => new RTDSensor(device));

or the more LINQ-based approach:

IEnumerable<RTDSensor> x = _devices.Select(device => new RTDSensor(device));

... but you can't just tell .NET to treat an object as if it were a more specific type than it actually is. To take it to a ridiculous length, what would you expect this to do:

object o = new object();
FileStream fs = (FileStream) o;
rs.ReadByte(); // Where from?

EDIT: I've assumed that you can't change what's in the _devices collection. If you can make sure that it contains objects of the appropriate type to start with, that's great - if not, you'll need to create the new objects later.

Jon Skeet
I think you missed the point of just adding the right type of object to the collection which would be much simpler than passing in the "base" object to the constructor of the "derived" object.
chrischu
You're assuming he can change how the collection is constructed though. I'm trying to address what I suspect is a more fundamental misunderstanding: that the OP believes cast will create a new object by calling the constructor.
Jon Skeet
Makes sense about the conversion. I'll use a new object and pass the base object in constructor.
Paul McCowat
+1 as the OP agrees with the answer given
kevchadders
+1  A: 

The problem could be with the way you're adding the devices to the _devices collection.

For example:

RTDSensor sensor = new RTDSensor(1);
_devices.Add(sensor);
RTDSensor retrievedSensor = (RTDSensor)_devices.Find(d => d.Id == 1);

should work. However if you add the sensor as an RTUDevice object you can't cast it to RTDSensor afterwards because it doesn't have the additional information that RTDSensor carries.

Referring to Jon Skeet's example you have to use

object o = new FileStream(path, filemode);
FileStream fs = (FileStream)o;

If you later want to cast o to a FileStream object.

chrischu
Would this mean the _devices collection is a list of base types and I'm adding derived types to this?
Paul McCowat
That's a really common approach to storing a collection of objects with common base type. However if you want to use the RTDSensor methods on all your objects in the collection is it of course better to just make it a collection of type RTDSensor instead of casting it every single time.
chrischu
This works, thanks. Items are coming from a database and I wanted to get the basic info into the base list which I can now do.
Paul McCowat
+1 here too for new accepted answer...
kevchadders