tags:

views:

269

answers:

7

I have two interfaces IHeaderRow, and IDetailRow

I then have an object that implements both RawRow:IHeaderRow, IDetailRow

I then need to cast it to HeaderRow which implements IHeaderRow.

But when I try, it ends up being null or giving an exception.

I can cast ObjectRawRow to either interface IHeaderRow, or IDetailRow

var ObjectIHeaderRow = ObjectRawRow as IHeaderRow;
var ObjectIDetailRow = ObjectRawRow as IDetailRow;

But I can not cast ObjectRawRow to HeaderRow , or ObjectIHeaderRow to HeaderRow.

It throws the error Cannot convert source type 'IA' to target type 'A'

I need to cast it into the actual class HeaderRow.

Thoughts?

EDIT:

Even though setting up an explicit cast took care of the issue I thought I'd provide an answer to the people wondering, WHY I was doing what I was.

In short, I'm sequentially processing a file. Line by line. I read the row into RawRow, and until I look at a few values, I don't actually know what type of row it is going to be. I then wanted to cast it to the proper type.

+1  A: 

You cannot cast ObjectRawRow to HeaderRow unless one inherits from the other.

Interfaces have nothing to do with it.


Consider:

class Shape
interface IHasCorners
class Rectangle : IHasCorners, Shape
class Triangle : IHasCorners, Shape


Rectangle myRectangle = new Rectangle();
Triangle myTriangle = new Triangle();

//upcasts
Shape s = (Shape)myRectangle;
IHasCorners hc = (IHasCorners)myRectangle;

//downcasts
Rectangle r2 = (Rectangle)s;
r2 = (Rectangle)hc;

//upcasts
s = (Shape)myTriangle;
hc = (IHasCorners) myTriangle;

//these downcasts won't work
//the variables now reference a Triangle instance
Rectangle r3 = (Rectangle)s;
r3 = (Rectangle)hc;
David B
+5  A: 

You can only implicitly cast objects to types they inherit from or implement - since RawRow doesn't derive from HeaderRow, it's not possible.

Depending on your requirements, you could overcome this by writing an explicit conversion operator, creating a HeaderRow constructor that accepts a RawRow as its prototype, or by modifying your code to operate on an IHeaderRow.

Jeff Sternal
Conversion Operator worked great. This has troubled me so many times.Everything works great as interfaces, until I try and serialize the object. I then end up changing it's definition from interfaces to classes...and then can't actually populate it because of this casting restriction
Chad
Little language quibble, the OP is trying to do an *explicit* cast, and needs to implement a *user-defined implicit conversion operator*.
Steven Mackenzie
@Steven - interesting, why do you say that? I hadn't originally specified an explicit operator, but then changed it for some reason - I guess it sounded like the conversion might be lossy or might potentially throw an exception. I suppose it's not clear from the question, so I may just change it back to unspecified.
Jeff Sternal
@Jeff: The OP's cast is explicit because he asks for it (using the *as* keyword). On point two... I'm not sure why I claimed he needed an implicit conversion operator... that's assuming he wants to change his code to do an implicit cast (which really he doesn't). If he retains his explicit cast he should indeed create an explicit conversion operator as you said. My apologies for the crazy assertion :)
Steven Mackenzie
+1  A: 

You will not be able to make this cast unless there is an inheritance relationship between the types. If that is not possible then the best you can do is create an explicit conversion operator that allows you to cast one type as another type.

If you do create an explicit conversion you should understand that this will be slower than casting as you will be invoking an method that will do work as opposed to casting which only changes the reference type and doesn't change any of the memory on the heap.

Consider this example that doesn't compile:

class Example
{
    static void Main()
    {
        Foo foo = new Foo();
        Bar bar = (Bar)foo;
    }
}

class Foo { }
class Bar { }

Since there is no inheritance relations between the types nor is there an explicit conversion from Foo to Bar this cannot compile.

But adding an explicit conversion allows it to compile:

class Example
{
    static void Main()
    {
        Foo foo = new Foo();
        Bar bar = (Bar)foo;
    }
}

class Foo
{
    public static explicit operator Bar(Foo foo)
    {
        return new Bar();
    }
}
class Bar { }
Andrew Hare
Gah, sorry Andrew, I edited yours instead of mine - then rolled yours back. D:
Jeff Sternal
@Jeff - No worries! I have done it many times before myself :)
Andrew Hare
A: 

You can only cast an instance to a particular class if the object is actually an instance of that class (or is derived from that class).

It is not possible to cast an instance of class A to completely unrelated class B (which is what you're trying to do), even if they implement the same interfaces.

SLaks
A: 

You can use the explicit keyword to create methods that will be called when you try to cast from IA to A. The reason it doesn't work without you writing your own method is because the compiler doesn't know what to do with the values that aren't being provided.

Joel
Cast operators are called at compile time. They might not help here.
SLaks
+2  A: 

First, why do you need to do such a weird cast? There's probably another design for what you're trying to do.

Second, the reason you can't do the cast is because a RawRow isn't an HeaderRow. The only guarantee it makes is that it implements IHeaderRow. The problem is that it has a bunch of other stuff too, stuff that HeaderRow doesn't have. And vice versa - HeaderRow probably has a bunch of stuff that ObjectRawRow doesn't have.

Imagine your classes look like this:

interface IHeaderRow
{
    string GetText();
}

class HeaderRow : IHeaderRow
{
    public string GetText()
    { 
        return "My Label";
    }

    public int GetFoo()
    {
        return 42;
    }
}

class ObjectRawRow : IHeaderRow
{
    public string GetText()
    {
        return "My Raw Label";
    }
}

Now if you do this, you're ok:

ObjectRawRow row = new ObjectRawRow();
IHeaderRow header = row as IHeaderRow;
string label = header.GetText();    // fine, since GetText is guaranteed to exist

But try this on for size:

ObjectRawRow row = new ObjectRawRow();
HeaderRow header = row as HeaderRow;
int magic = header.GetFoo();       // BOOM! Method doesn't exist,
// because the object isn't really a HeaderRow under the covers.
// It's still really an ObjectRawRow. What do you do now? Crash hard is what.

And that's why you can't cast outside of the inheritance tree.

Tesserex
Your example is unclear - can you show why GetText can't be called on a variable of type HeaderRow that references an instance of ObjectRawRow.
David B
It's not that you technically couldn't, I guess, but the compiler won't even let you get close enough to try. It won't even let you cast an ObjectRawRow to a HeaderRow, because then you're setting yourself up for the possibility of crashing the CLR. The "contract" that it's safe to call any and all HeaderRow methods must be upheld at all times.
Tesserex
"The compiler won't" : HeaderRow hr = (HeaderRow)(object) someObjectRawRow;
David B
+4  A: 

Why do you need to cast it to a HeaderRow in the first place? If IHeaderRow produced the api that a HeaderRow implements, than you should just be able to act on IHeaderRow "objects" using the defined methods.

The point of an interface is so that you can treat a grouping of different objects as a similar type. Not so that you can cast different objects between classes that are not linked by inheritance.

unholysampler