GurdeepS
GurdeepS

Reputation: 67223

Casting between two types derived from the (same) interface

I have an interface and two types that derive from it.

However, I cannot do the following:

B objectB = (B) objectA

Where B derives from Interface1 (I am making up the name of classes but the point still stands), and likewise for objectA (which is of type A). I get the following error message:

Cannot cast expression of type A to B.

Both types are deriving from the interface, what am I missing?

Upvotes: 16

Views: 22223

Answers (10)

Rafi Henig
Rafi Henig

Reputation: 6424

With the help of a static generic method defined in the interface, a desired result could be achieved using a reflection:

 public interface IPerson
 {
     string Name { get; set; }
 
     static TDest ChangeType<TDest, TSource>(TSource source) where TSource : IPerson where TDest : IPerson, new()
     {    
         var instance = new TDest();
         foreach (var property in typeof(IPerson).GetProperties())
         {
             property.SetValue(instance, property.GetValue(source));
         }
         return instance;
     }
 }
 

Classes implementing IPerson:

public class Worker : IPerson
{
    public string Name { get; set; }
}

public class Manager : IPerson
{
    public string Name { get; set; }
}

Usage:

var worker = new Worker { Name = "John" };
var manager = IPerson.ChangeType<Manager, Worker>(worker);

Alternatively using Aggregate:

public interface IPerson
{
    string Name { get; set; }

    static TDest ChangeType<TDest, TSource>(TSource source) where TSource : IPerson where TDest : IPerson, new()
    {
        return typeof(IPerson).GetProperties()
                              .Aggregate(new TDest(), (dest, prop) => {
                                  prop.SetValue(dest, prop.GetValue(source));
                                  return dest;
                              });
    }
}

Upvotes: -1

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112352

An object is assignable to an ancestor (direct or indirect base type) or to an interface it implements, but not to siblings (i.e. another type deriving from a common ancestor); however, you can declare your own explicit conversions:

class FooObject : IObject
{
    public string Name { get; set; }
    public int Value { get; set; }

    public static explicit operator FooObject(BarObject bar)
    {
        return new FooObject { Name = bar.Name, Value = bar.Value };
    }
}

class BarObject : IObject
{
    public string Name { get; set; }
    public int Value { get; set; }

    public static explicit operator BarObject(FooObject bar)
    {
        return new BarObject { Name = bar.Name, Value = bar.Value };
    }
}

Now you can write

var foo = new FooObject();
var bar = (BarObject)foo;

or

var bar = new BarObject();
var foo = (FooObject)bar;

without getting errors.

You can also create implicit conversions, if it feels natural. E.g. int is implicitly convertible to double: int i = 5; double x = i;.

(This is also an answer to the closed question How do I cast Class FooObject to class BarObject which both implement interface IObject?).

Upvotes: 14

Anthony Pegram
Anthony Pegram

Reputation: 126854

You cannot cast or convert from A to B if all they share is a common interface unless you actually define your own conversion operator, assuming you control the source for one of the types, or use another provided user-defined conversion supplied by someone who does control the source. (However, such user-defined conversions would not preserve the original object. One object goes into the conversion, a different object comes out.)

You can convert from A to Interface1, and B to Interface1. But two types simply sharing a common parent does not make those two types convertible to one another.

A a = new A(); 
B b = new B();
Interface1 obj1 = a; // legal
Interface1 obj2 = b; // legal
B obj3 = (B)a; // not legal, a is simply not a B

tobias86 put in well in a comment below, you have a cat and a dog. Both derive from Animal. But a cat just isn't a dog.


As an expansion, you might be struggling with how and why you would use an interface. You do not use an interface to substitute an A for a B, or a B for an A. You use it to substitute either A or B for Interface1. It's the interface you expect, and the A or B you might supply. Given:

public void DoSomething(Interface1 obj) { } // expects 
DoSomething(new A()); // you can supply A

Or

public Interface1 GetSomething() // callers expect to get 
{
    return new B(); // you can supply a B
}

It's the interface you are programming towards, The A and B are merely implementations. You might be thinking you can pass a B to something that expects A. The expectation possibly needs to change.

Upvotes: 7

Nuffin
Nuffin

Reputation: 3972

Imagine the following setup:

public interface Human
{
    bool Male { get; }
}

public class Man : Human
{
    public bool HasABeard { get { return true; } }

    public bool IsMale { get { return true; } }
}

public class Woman : Human
{
    public bool IsMale { get { return false; } }

    public List<Pair<Shoe>> Shoes { get; set; }
}

What would you expect the compiler to produce from the following code? What will the output be?

Man a;
Woman b = new Woman();
a = (Man)b;

Console.WriteLine(a.HasABeard ? "Beard ON" : "Beard OFF");

Upvotes: 0

Rune FS
Rune FS

Reputation: 21742

When casting from A to B B must be a super type for A or the runtime type of the object must be B

that is if you have

class A : B{}

you can cast an object of compile time type A to B. You can also cast a type of B to A if the runtime type of the object is A

in your case the two types does not share super-subtype relationship. They only share a common super type but that's not sufficient.

As an example of why this can't work (generically) how would you have the compiler cast from Point[] to a Dictionary<string,HashSet<byte>>? (both implement IEnumerable)

Upvotes: 1

Aren
Aren

Reputation: 55946

You need to cast as the interface.

interface IBase { }
class A : IBase { }
class B : IBase { }

With this, the only thing the two types have in common is the interface members. B might have items that A does not.

A a = new A();
B b = new B();

IBase aBase = a;
IBase bBase = b;

You can then call anything on the IBase Interface.

Upvotes: 1

undefined
undefined

Reputation: 34248

You can only cast them to the interface type. A is not B but they are both I. this means you can take A and cast to I or B and cast to I but not B and cast to A

Upvotes: 0

Daniel Daranas
Daniel Daranas

Reputation: 22624

  1. Types do not derive from an interface. They implement an interface.
  2. The fact that both an Elephant and a Spider are Animals doesn't mean that you can convert one to the other.

Upvotes: 62

John Saunders
John Saunders

Reputation: 161773

What you want to do doesn't make sense. objectA is not a B.

Upvotes: 0

Marc Gravell
Marc Gravell

Reputation: 1062790

The fact that both types implement the same interface (or have the same base-type, for that matter) does not make them interchangeable; an A is always an A, and a B is always a B. In an inheritance chain, an object can be cast as itself or any parent type. You have:

A : ISomeInterface
B : ISomeInterface

which lets you cast an A as A or ISomeInterface, and a B as B or ISomeInterface

or (depending on your meaning of "derived from")

SomeBaseType
 > A
 > B

which lets you cast an A as A or SomeBaseType, and a B as B or SomeBaseType

(plus object, in each case)

Upvotes: 4

Related Questions