【发布时间】:2012-01-13 22:26:47
【问题描述】:
我有一个接口和两种派生自它的类型。
但是,我不能执行以下操作:
B objectB = (B) objectA
其中 B 派生自 Interface1(我正在编造类的名称,但重点仍然存在),同样适用于 objectA(属于 A 类型)。我收到以下错误消息:
无法将 A 类型的表达式转换为 B。
这两种类型都是从接口派生的,我缺少什么?
【问题讨论】:
我有一个接口和两种派生自它的类型。
但是,我不能执行以下操作:
B objectB = (B) objectA
其中 B 派生自 Interface1(我正在编造类的名称,但重点仍然存在),同样适用于 objectA(属于 A 类型)。我收到以下错误消息:
无法将 A 类型的表达式转换为 B。
这两种类型都是从接口派生的,我缺少什么?
【问题讨论】:
【讨论】:
explicit operator。
OfficeWorker 转换为Boss 并希望传输他们实现的IEmployee 接口提供的所有属性。为什么 C# 没有为这种情况提供任何东西?就像 JSON-way-workaround 已经做的那样:Boss bossObj = JsonSerializer.Deserialize<Boss>(JsonSerializer.Serialize(officeWorkerObj)),尽管在这种情况下它们既不必共享接口也不必从同一个类派生。
一个对象可以分配给一个祖先(直接或间接基类型)或它实现的接口,但不能分配给兄弟姐妹(即从共同祖先派生的另一种类型);但是,您可以声明自己的显式转换:
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 };
}
}
现在你可以写了
var foo = new FooObject();
var bar = (BarObject)foo;
或
var bar = new BarObject();
var foo = (FooObject)bar;
不会出错。
如果感觉自然,您还可以创建implicit 转换。例如。 int 可隐式转换为 double: int i = 5; double x = i;。
(这也是对封闭问题How do I cast Class FooObject to class BarObject which both implement interface IObject?的回答)。
【讨论】:
如果它们共享的只是一个公共接口,则您不能从A 转换或转换为B,除非您实际定义自己的转换运算符,假设您控制其中一种类型的源,或者使用另一个提供的用户-由确实控制源的人提供的定义转换。 (但是,这种用户定义的转换不会保留原始对象。一个对象进入转换,另一个对象出来。)
您可以将A 转换为Interface1,将B 转换为Interface1。但是两种类型只是共享一个共同的父对象并不能使这两种类型相互转换。
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 在下面的评论中说得很好,你有一只猫和一只狗。两者都派生自Animal。但猫不是狗。
作为扩展,您可能会为如何以及为何使用界面而苦恼。您不使用接口来代替 A 代替 B,或使用 B 代替 A。您使用它来代替 A 或 B 代替 Interface1。这是您期望的接口,以及您可能提供的 A 或 B。给定:
public void DoSomething(Interface1 obj) { } // expects
DoSomething(new A()); // you can supply A
或者
public Interface1 GetSomething() // callers expect to get
{
return new B(); // you can supply a B
}
这是您正在编程的接口,A 和 B 仅仅是实现。您可能认为可以将 B 传递给期望 A 的事物。期望可能需要更改。
【讨论】:
这两种类型都实现相同的接口(或具有相同的基类型,就此而言)这一事实并不能使它们可互换; A 始终是 A,B 始终是 B。在继承链中,对象可以转换为自身或任何父类型。你有:
A : ISomeInterface
B : ISomeInterface
这让您可以将A 转换为A 或ISomeInterface,并将B 转换为B 或ISomeInterface
或(取决于您对“源自”的含义)
SomeBaseType
> A
> B
这让您可以将A 转换为A 或SomeBaseType,并将B 转换为B 或SomeBaseType
(加上object,在每种情况下)
【讨论】:
当从 A 转换为 B 时,B 必须是 A 的超类型或对象的运行时类型必须是 B
如果你有
class A : B{}
您可以将编译时类型 A 的对象强制转换为 B。如果对象的运行时类型为 A,您也可以将 B 类型强制转换为 A
在您的情况下,这两种类型不共享超子类型关系。它们只共享一个共同的超类型,但这还不够。
作为一个例子,说明为什么这不能(通常)工作,您如何将编译器从Point[] 转换为Dictionary<string,HashSet<byte>>? (都实现了 IEnumerable)
【讨论】:
B 是A 的超类型,则从A 到B 的强制转换是合法的并且保证成功;这是合法的,但如果B 是一个子类型,它可能会在运行时失败。不过,有趣的一点是,在没有可识别子类型/超类型排列的泛型类型之间禁止转换,即使这种转换可能成功。给定泛型类型 T:Control,不能将 T 的实例转换为 Button,也不能将 Button 的实例转换为 T,尽管可以将其中一个实例转换为 Control,然后将该转换的结果转换为另一种类型。跨度>
x 是 T 类型的变量,从 Control 派生,它恰好持有 DerivedButton 类型的实例(暗示 T 是 DerivedButton 或超类型其中)。如果T 是Control,则将x 转换为按钮是向上转换,可能成功也可能不成功;如果T 是DerivedButton,那么将x 转换为按钮是一个向下转换,它总是会成功。我发现有趣的是,编译器不能将从T 转换为Button 的转换视为可能成功也可能不成功,如果失败应该抛出异常。
Window?泛型被设计为类型安全在编译时,如果允许的话,情况就不是这样了。
你想做的事情没有意义。 objectA 不是 B。
【讨论】:
您只能将它们转换为接口类型。 A 不是 B,但它们都是 I。这意味着您可以将 A 转换为 I 或 B 转换为 I,但不能将 B 转换为 A
【讨论】:
您需要转换为接口。
interface IBase { }
class A : IBase { }
class B : IBase { }
有了这个,这两种类型唯一的共同点就是接口成员。 B 可能有 A 没有的项目。
A a = new A();
B b = new B();
IBase aBase = a;
IBase bBase = b;
然后您可以在IBase 接口上调用任何东西。
【讨论】:
想象以下设置:
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; }
}
您希望编译器从以下代码中生成什么?输出会是什么?
Man a;
Woman b = new Woman();
a = (Man)b;
Console.WriteLine(a.HasABeard ? "Beard ON" : "Beard OFF");
【讨论】: