关于C#中对象引用类型转换的问题?

关于C#中对象引用类型转换的问题?,c#,object,types,reference,casting,C#,Object,Types,Reference,Casting,我有一个关于对象类型转换的问题。 假设我们有: A a = new A(); Object o = a; 正如我所知,后面发生的事情是编译器将复制a的地址并存储在o变量的内存区域中。那么,我们可以说a和o引用的是同一个对象 如果我们这样做: String s = "abc"; int a = (int)s; 然后我了解到编译器无法将字符串值复制到int内存区域 但如果我们有: A a = new A(); B b = (B)a; 这在编译时可能还可以。但是,可能会发生运行时错误,类似于“无

我有一个关于对象类型转换的问题。 假设我们有:

A a = new A();
Object o = a;
正如我所知,后面发生的事情是编译器将复制a的地址并存储在o变量的内存区域中。那么,我们可以说a和o引用的是同一个对象

如果我们这样做:

String s = "abc";
int a = (int)s;
然后我了解到编译器无法将字符串值复制到int内存区域

但如果我们有:

A a = new A();
B b = (B)a;
这在编译时可能还可以。但是,可能会发生运行时错误,类似于“无法转换…”

所以,我不明白在记忆中究竟发生了什么,使得上述铸造无法进行。它只是将a的地址复制到b的内存区域吗?如果是,为什么不可能? 或者它将复制A的所有成员以替换B的所有成员

多亏了

编译器进行了静态类型检查,这意味着如果A和B不属于同一继承层次结构,它将不允许在两者之间进行转换。

考虑一下,如果它们不属于同一层次结构,即使编译器允许您将A的对象强制转换为B的类型,因为A不从B或其继承者继承,您可能希望在强制转换的对象上调用B类型的方法之一,并且在运行时会失败

class A { }
class B { 
    void Foo() { }
}

A a = new A();
B b = (B)a;      // Compiler Error

// Hypothetically, if above was allowed, the below would ALWAYS fail at runtime
// Since there is no way the object "b" can handle this call.
b.Foo();

不过,这里存在一个有趣的问题,如果B是接口,编译器将允许强制转换发生,即使它们不属于同一继承树:

class A { }
interface B { 
    void Foo();
}

A a = new A();
B b = (B)a;      // Compiler lets this happens 

// Even though A does not implement B, but still one of the base classes of A 
// might have implement B and A inherits that so it might be able to handle this
b.Foo();

这是因为A可能来自不同的层次结构树,但仍有可能A或其某个基类实现了B,因此您可能有该强制转换的一个点,编译器将允许这样做。

正如Morteza所解释的,C编译器将进行静态类型检查,以确定强制转换是否可行

至于运行时会发生什么,您可能需要查看生成的IL以获得更好的想法。对于引用类型,您的cast可能会成为IL
castclass
指令。这将导致运行时检查实际的对象类型,以查看强制转换是否有效。如果是,引用(指向对象的32位或64位指针)将按预期复制到变量赋值中。如果否,则抛出InvalidCastException


对于引用类型对象,不会复制单个成员。仅复制32/64位引用。对于值类型的对象,单个成员(字段)在赋值过程中被复制。

学习像C这样的托管语言时,至少在开始时,忘记对象占用内存空间并将内存从一个地方复制到另一个地方的想法是很有帮助的。这与面向对象的抽象基本上是无关的,并且会分散对概念的注意力。非常感谢。我现在一切都很清楚了。我还有一个问题。如果类A:B,那么我们可以按如下方式进行转换:A=(B)B;。这在内存中究竟起什么作用?它将复制B的地址并存储在变量中?没问题。是的,那么“a”将是指由“b”引用的对象(即,b变量的内存值将被复制到“a”变量中,该变量只是对象所在堆中的一个地址)。“对于引用类型的对象,永远不会复制单个成员。只复制32/64位引用。对于值类型的对象,单个成员(字段)在作业过程中被复制。”=>这是什么意思?我真的不明白。您能详细说明一下吗?当引用类型被实例化时,内存中的对象布局(字段、vtable等)被分配到托管堆上。分配给它的任何变量实际上都是引用(指向对象堆地址的32/64位指针)。当值类型(从System.value或任何C#“struct”派生的任何内容)被实例化时,分配给它的变量就是内存中的对象布局(所有字段)。没有像引用类型那样的间接寻址。因此,当您将另一个变量分配给第一个变量时,所有字段都会被复制,因为每个字段都是其自己的内存对象布局。例如,如果您有一个引用类型类a{int v1;int v2;int v3},并且执行“a a1=new a();”,则一个由12个字节(加上开销)组成的新对象将被放置在堆上的地址0x1234处。变量a1将具有32/64位值0x1234,该值是对对象的引用。如果执行“a2=a1”,变量a2将具有32/64位值0x1234。仍然只有一个对象,a1和a2都引用它。如果您有一个值类型struct B{int v1;int v2;int v3;}并执行“B b1=new B();”,则变量b1将由代表3个整数字段的12个字节组成。如果执行“b2=b1”,变量b2将由12个字节组成,这些字节是b1的副本。但如果您选择“b2.v1=43”,它将不会影响b1.v1,b1.v1的默认值仍然为零。