Casting between classes that share the same interf

2020-02-07 00:55发布

问题:

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:

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.



回答2:

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.



回答3:

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.



回答4:

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;


回答5:

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 { }


回答6:

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.



回答7:

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.