在C#中,对引用类型的成功强制转换操作最终会在堆

时间:2017-11-18 13:11:19

标签: c# casting

我正在查看关于强制转换的MSDN文档中的C#代码。

在此代码中,Animal是基类,Giraffe是Animal类的派生类。

问题:对引用类型的隐式和显式转换是否会创建新的堆存储位置?例如,变量a将指向变量g指向的相同堆位置,或者a将指向由于隐式转换而创建的堆上的新位置,答案背后的原因是什么?

我的猜测是否定的,因为C#代码会消耗过多的堆内存,这些内存经常进行隐式转换。但是,我不确定答案,这是原因。

此外,对于相同代码中的显式强制转换,似乎g2将指向与g相同的位置,因此对引用类型的成功显式强制转换操作不会创建新的堆位置。

从MSDN投射操作C#代码

// Create a new derived type.  
Giraffe g = new Giraffe();  

// Implicit conversion to base type is safe.  
Animal a = g;  

// Explicit conversion is required to cast back  
// to derived type. Note: This will compile but will  
// throw an exception at run time if the right-side  
// object is not in fact a Giraffe.  
Giraffe g2 = (Giraffe) a;

动物和长颈鹿类C#代码

class Animal
{
    bool IsFourLegged
    {
        get;
        set;
    }

    bool CanSpeak
    {
        get;
        set;
    }
}

class Giraffe : Animal
{
    string Country
    {
        get;
        set;
    }

    string StripeColor
    {
        get;
        set;
    }
}

2 个答案:

答案 0 :(得分:2)

不,它不会创建新的托管对象实例。它只会在相同的实例上创建一个新的引用(在调用堆栈上,或者在CPU寄存器中)。

当然我在谈论两种引用类型之间的转换(其中一种是另一种的基类,或者至少有一种是接口类型),C#规范称为引用转换 。其他类型的演员表是不同的。

正如评论中所指出的,用户定义的转换运算符也可能存在于两个class类型之间(仅当两者都不是另一个的基类时)。当然,这样的强制转换不是引用转换,而是返回不同的实例(或null)!评论者的例子:

string str = "Name";
XName name = str;

最后一行创建一个类型为XName的新对象实例(或重新使用它可能拥有的现有XName实例)。无论如何,XName永远不能与string相同!这种转换实际上可以做任何事情,因为它实际上只是一个方法调用(如运行时所见),成员public static implicit operator XName(string expandedName),实际上在CIL中命名为op_Implicit或类似。

答案 1 :(得分:2)

简短版本:Object.ReferenceEquals将显示强制转换不会创建新对象,只是对同一对象的另一个引用。

更新:我原本以为这可能是通过实现显式转换实现的,如果你想由于某种原因让它发生,但事实并非如此 - 代码将无法编译。错误消息是:

CS0553: User-defined conversion `Giraffe.explicit operator  
Animal(Giraffe)' cannot convert to or from a base class

https://ideone.com/G1k7Q2

另请参阅引用c#规范的https://stackoverflow.com/a/17459267/234954