我正在努力通过投射来理解这种情况。 出于演示目的,我创建了这些类:
public interface IFoo {}
public class Foo : IFoo
{
public string Name { get; set; }
}
public class Bar : Foo
{
public string Surname { get; set; }
}
现在,在我的Main方法中,我创建了一个返回IFoo的静态方法:
class Program
{
static void Main()
{
IFoo iFoo = GetIFoo();
Foo foo = (Foo)iFoo;
//the GetType ext method is executed and it returns Bar
if(foo is Foo)
Console.WriteLine($"foo is of type: {foo.GetType()}");
Console.ReadLine();
}
static IFoo GetIFoo()
{
var bar = new Bar
{
Name = "MyName",
Surname = "MySurname"
}
return bar;
}
}
问题是:即使我在GetIFoo方法中创建一个Bar,为什么如果我将IFoo转换为Foo,foo的类型是Bar而不是Foo?< / p>
答案 0 :(得分:2)
为什么如果我将
iFoo
转换为Foo
,foo
的类型为Bar
而不是Foo
?
这是因为强制转换会改变静态类型。它对动态类型没有影响。 foo
的静态类型为Foo
,但其动态类型为Bar
。
每个变量都有一个编译器已知的类型(静态类型)和运行时已知的类型(动态类型)。在某些情况下,这两种类型是相同的,但它们不必一直相同。
静态类型是变量的声明类型。动态类型是在将对象分配给该变量后通过调用GetType
获得的。
当一个变量的静态类型与其动态类型不匹配时,您构建了一个示例:iFoo
的静态类型为IFoo
,但其动态类型为Bar
。这非常好,因为Bar
与IFoo
兼容,因为它实现了接口,也与Foo
兼容,因为它扩展了类。
答案 1 :(得分:1)
这真的是inheritance 101:创建的对象是Bar
,而广告只会影响您查看的方式,而不会影响真正的
您可以在此处将其视为IFoo
:
IFoo iFoo = GetIFoo();
在这里,你刻意将其视为Foo
。它不是Foo
或其任何后代,您在运行时获得InvalidCastException
。
Foo foo = (Foo)iFoo;
Bar
个实例可以合法地用作Bar
,Foo
和IFoo
(以及Object
为了完整性)。无论您如何查看实例,它仍然是Bar
。
答案 2 :(得分:1)
Type.GetType
为您提供基础实例的类型(即new
之后指定的类型),无论哪种类型的变量持有它。此外,foo is Foo
不仅true
给出了Foo
实际实例为Foo
,还提供了从class Emitter<O,
OK extends keyof O,
OV extends O[OK] & {(data:D):void},
D> {
constructor(public options: O,
public name: OK) { }
emit(data: D) {
this.options[this.name] &&
this.options[this.name](data);
}
}
let options: {
onSave: (data: string) => void,
size: number
};
let myEmitter1 = new Emitter(options, "onSave");
// I need error next line about "size" type.
let myEmitter2 = new Emitter(options, "size");
派生的每种类型。
答案 3 :(得分:1)
因为 GetType()返回对象类型。当您明确地将派生类转换为基类时,您没有创建新实例,您只对现有对象有新的类型引用。您可以使用以下代码查看它:
IFoo iFoo = GetIFoo();
Foo foo = (Foo)iFoo;
Console.WriteLine(ifoo.Equals(foo));
答案 4 :(得分:1)
您在对象的运行时类型与其编译时类型之间感到困惑。
- public
- css
- bootstrap.css
- src
- myapp.cr
- views
- layout
- standard.ecr
在上面一行中,IFoo iFoo = GetIFoo();
的运行时类型仍为iFoo
。这是对象的真实类型。但是如果通过类型为Bar
的引用访问Bar
对象,则编译器只知道类型为IFoo
,因此称为引用所指向的对象的编译时类型{ {1}}。请注意,当执行IFoo
中的iFoo
语句时,编译器丢失了此类型信息。
return bar
再往下,您可以将其转换为GetIFoo
层次结构中所需的任何类型,上面包括Foo foo = (Foo)iFoo;
的任何内容都将成功,编译时类型将基于您键入的内容 - 把它铸成了。但是,运行时类型仍将保持不变,即Bar
答案 5 :(得分:0)
因为行:
Foo foo = (Foo) iFoo;
你不转换:Bar
同时是Foo
和IFoo
。因此,当您转向(Foo)
时,C#解释器会注意到iFoo
是Bar
,因此保持对象不变。关键是,从现在开始,编译器将假定foo
确实是Foo
对象,但它可以是任何扩展Foo
类的类。
让我们通过以下示例更清楚:
interface IDriveable {} // IFoo
class Car : IDriveable { // Foo
string Name {get; set; }
}
class Volkswagen : Car { // Bar
bool CorrectPolution { get; set; }
}
现在,如果您构建Volkswagen
,那么十个显然是Car
和IDriveable
。但是说:Car car = (Car) volkswagen;
。你不改变大众汽车,你现在只让编译器知道car
是Car
对象。