Reputation: 67223
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
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
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
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
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
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
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
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
Reputation: 22624
Upvotes: 62
Reputation: 161773
What you want to do doesn't make sense. objectA
is not a B
.
Upvotes: 0
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