16

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?

Daniel Daranas
  • 22,454
  • 9
  • 63
  • 116
GurdeepS
  • 65,107
  • 109
  • 251
  • 387

10 Answers10

60
  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.
Daniel Daranas
  • 22,454
  • 9
  • 63
  • 116
  • 11
    I really like the brevity of this answer - gets all the necessary info across simply, clearly and cleanly – Marc Gravell Jan 13 '12 at 22:33
  • 6
    The latter is a particularly important point; Spider-Pigs were dangerous enough already. – Dan Bryant Jul 10 '13 at 13:27
  • 4
    Explaining why it isn't possible by default doesn't count as a full answer. You could've mentioned a solution like creating an ```explicit operator```. – Mason Mar 08 '19 at 07:05
  • But I want to convert `OfficeWorker` to `Boss` and want to transfer all properties that their implemented `IEmployee` interface provides. Why does C# offer nothing for such case? Like the JSON-way-workaround already does: `Boss bossObj = JsonSerializer.Deserialize(JsonSerializer.Serialize(officeWorkerObj))`, despite the fact that in this case they neither have to share an interface nor derive from the same class. – Martin Schneider Nov 16 '21 at 17:38
  • @MartinSchneider I see your point. But in the general case, this doesn't look so easy. Imagine the interface "IPrintable". Maybe a Grid is printable, and a Graph is printable, too; they both implement the interface. But that doesn't mean you can convert a Grid to a Graph. You could write a constructor for one of the classes which takes the other as an argument, of course. In your example, there may be other properties of a Boss which you need to initialize in the constructor and are not included in the interface IEmployee, nor are they properties of OfficeWorker. – Daniel Daranas Nov 17 '21 at 18:22
14

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?).

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
6

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.

Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
  • Not fair... not only his name is similar to mine but so are his ideas (see my answer) >. – Nuffin Jan 13 '12 at 22:41
  • nice answer however OP is talking about casting which does not working in his case. Converting could however work (the syntax is the same the semantics different) – Rune FS Jan 13 '12 at 22:46
  • 1
    @RuneFS, see [this article](http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx) for how I'm using it. It's a representation-preserving conversion in this case. The OP is simply trying to cast or convert from one type to another when all they share is a common interface. Most answers here simply point out that error. None have gone so far as to show how to make that conversion *legal*, which could certainly be done through user-defined conversion operators, but those would be representation-changing, the objects would not be the same. – Anthony Pegram Jan 14 '12 at 02:09
3

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)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
1

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)

Rune FS
  • 21,497
  • 7
  • 62
  • 96
  • As you note, a cast from A to B is legal and guaranteed to succeed if `B` is a supertype of `A`; it is legal, but may fail at runtime, if `B` is a subtype. An interesting note, though, is that casting is forbidden between generic types which have no identifiable subtype/supertype arrangement, even when such casts might succeed. Given generic type T:Control, one cannot cast an instance of T to Button, nor an instance of Button to T, though one could cast an instance of either to Control and then cast the result of that cast to the other type. – supercat Jan 13 '12 at 22:46
  • supercat the same rule applies in the generic case. There's no super-subtype relationship between Button and T. That T is instantiated to Control is irrelevant. Howver a constraint on T to Control will change that – Rune FS Jan 13 '12 at 22:48
  • @RuneFS: I suppose supercat was talking about generic constraints. But even so you can't make sure that now and for ever there will never be an instance of that type/method created/invoked with another type parameter than T2:Button, so even then it will generate a compiler error. – Nuffin Jan 13 '12 at 22:59
  • @Tobias: If `x` is a variable of type T, derived from Control, which happens to hold an instance of type `DerivedButton` (implying that `T` is `DerivedButton` or a supertype thereof). If `T` is `Control`, a cast of `x` to button is an upcast which may or may not succeed; if `T` is `DerivedButton`, then a cast of `x` to button is a downcast which will always succeed. I find it interesting that the compiler can't regard the cast from `T` to `Button` as being one that may or may not succeed and should throw an exception if it fails. – supercat Jan 13 '12 at 23:04
  • That might be true, but what will happen to that code if I call it (from an external library, which wasn't present when that particular hypothetical library we are talking about was built) with `T` = `Window`? Generics are designed to be type safe _at compile time_ which wouldn't be the case if that was allowed. – Nuffin Jan 13 '12 at 23:10
  • @supercat the point you are missing is that T is never Control. T is T. In a closed generic type Control can be substituted for T. But at that point the type checking of the generic type is already done. What ever the code in the generic class is, it has to be valid for all possible substitutions of T. This is not related to casts though but to all code in a generic class or method. It's one of the points where a generic class is very different from a C++ template. What you are talking about is the case with templates. But a seperate class is created for each unique instantiation of a template – Rune FS Jan 14 '12 at 12:58
  • @RuneFS: My point is that even if types T and U are unknown, I don't see any reason why the CLI couldn't allow one to specify a cast from T to U, with the proviso that with some combinations of T and U the cast would fail for any non-null reference held in the T. I would guess that the CLI doesn't have the features necessary to make such a thing work well with structs (where the logical behavior would be to replace the code for the cast with "throw exception" if the types are incompatible). Conceptually it would seem useful to be able to, for example,... – supercat Jan 15 '12 at 18:18
  • ...have a routine which, given an instance of generic type T, could call IDisposable.Dispose if it implements IDisposable, without requiring boxing. Given a struct which is known to implement IDisposable, one can pass it as a constrained generic parameter, without boxing, to a static routine that can call Dispose. It would be nice to be able to write code which could call the constrained generic when types are compatible and throw an exception if not; if the code is never actually executed with incompatible types, the exception would never occur. – supercat Jan 15 '12 at 18:24
  • you can do that simply use the `as` operator (or cast to object in between) or use dynamic. I find the value of said feature very low since there's multiple ways of accomplishing the same. I'd low to be able to do generic programing as with c++ templates, which would also include something like what you suggest, but that would be irrelevant to me compared to the rest of the generic programming toolbox – Rune FS Jan 15 '12 at 19:13
1

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.

Aren
  • 54,668
  • 9
  • 68
  • 101
0

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;
                              });
    }
}
Rafi Henig
  • 5,950
  • 2
  • 16
  • 36
0

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

John Saunders
  • 160,644
  • 26
  • 247
  • 397
0

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

undefined
  • 33,537
  • 22
  • 129
  • 198
0

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");
Nuffin
  • 3,882
  • 18
  • 34