C#中引用类型的内存方面的显式转换解释

时间:2016-10-28 04:57:45

标签: c# c++ c casting reference

在MSDN中,"对于引用类型,如果需要从基本类型转换为派生类型,则需要显式强制转换#34;。

在wiki中,"在编程语言理论中,引用类型是指内存中对象的数据类型。另一方面,指针类型指的是存储器地址。引用类型可以被认为是隐式解除引用的指针。"这是C的情况。

如何在C#中考虑显式转换引用类型时如何解释内存存储过程?

2 个答案:

答案 0 :(得分:1)

对于大多数情况,参考变量和指针变量之间的差异确实不大。两者都指向内存中的位置。引用(或指针)变量的类型告诉compiller可以使用它执行哪些操作。

首先考虑C ++对象指针,而不是(主要)与基本类型(如int或byte)一起使用的C指针。它与C#中的几乎相同:

MyBaseClass* a = new MyBaseclass();
a->BaseMethod(); // Call method using -> operator (dereference and call)

MyBaseClass* b = new MyDerivedClass();
b->DerivedMethod(); // Error: MyBaseClass has no such method

// Proper C++-Style casting. 
MyDerivedClass* c = dynamic_cast<MyDerivedClass*>(b);
// Shortcut to the above, does not do the type test. 
// MyDerivedClass* c = (MyDerivedClass*)b; 
c->DerivedMethod(); // Ok

这几乎将1:1转换为C#,因此引用类型(从程序员的角度来看)只是具有已定义类型的指针。唯一可见的区别是C#中的直接C样式转换等同于C ++中的try_cast,这将确保您永远不会将错误的目标实例分配给引用变量。

因此,引用类型和指向对象的指针之间存在差异(C#是托管语言这一事实​​暗示了大部分内容):

  • 引用变量永远不能指向无效内存(除了NULL)。
  • 引用变量永远不会指向不属于其类型的对象。
  • 为参考变量赋值时,始终会测试该类型。
  • 对引用变量的强制转换需要检查目标对象是否为给定类型。

答案 1 :(得分:1)

引用对象存储在堆上,可以从代码中引用它们。堆上的对象属于给定类型。

从代码中,您可以创建对它的引用,并且可以将这些引用转换为某些其他类型。

现在,有几种情况在参考文章中有所描述。我将使用那里的例子来简化它。

<强> 1。隐含转换

当您没有在代码中特别要求时,会发生隐式转换。编译器必须自己知道如何执行此操作。

<强> 1.1。价值类型

如果您尝试强制转换的值的类型具有大小,那么允许您将其存储在内存大小中,该大小将构成要转换为的类型的大小,然后编译器将允许您这样做。这主要用于数值,因此请参考您引用的文章中的示例:

// Implicit conversion. num long can
// hold any value an int can hold, and more!
int num = 2147483647;
long bigNum = num;

因为int比'long'更长,编译器会让你这样做。

<强> 1.2。参考类型

假设您有以下类定义:

class Base {    

}

class Derived : Base {
    public int IntProperty { get; set; }
    public int CalculateSomething ()
    {
         return IntProperty * 23;
    }
}

然后您可以安全地进行如下转换:

Derived d = new Derived();
Base b = d;

这是因为您在堆上创建的对象d的类型为Derived,并且因为它是类型Base的派生类型,所以保证所有成员都Base 1}}。因此,转换引用并将Derived对象用作Base对象是安全的。因为派生的IS基础Derived : Base)。

<强> 2。明确转换

我们假设我们的项目中有另一个类:

class DerivedLike
{
    public int IntProp { get; set; }
    public int CalculateSomethingElse()
    {
        return IntProp * 23;
    }
}

如果我们写

DerivedLike dl = new DerivedLike();
Derived d = dl;

我们将从编译器中获取它不能隐式地将类型DerivedLike转换为Derived

这是因为两种引用类型完全不同,所以编译器不允许这样做。这些类型有不同的属性和方法。

<强> 2.1。实施显式转化

只要您不能自己从Derived类转换为Base类,您就可以在大多数其他情况下编写运算符。

如果想要继续从DerivedLike转换为Derived,我们必须在DerivedLike类中实现转换运算符。它是一个静态运算符,它告诉如何将一种类型转换为另一种类型。转换运算符可以是隐式的,也可以是显式的。显式将要求开发人员通过在括号中提供类型名称来显式地强制转换它。

在隐式和显式运算符之间进行选择的建议是,如果转换可能会抛出异常,那么它应该是显式的,以便开发人员有意识地完成转换。

让我们更改代码以满足该要求:

class DerivedLike
{
    public static explicit operator Derived(DerivedLike a)
    {
        return new Derived() { IntProperty = a.IntProp};
    }
    public int IntProp { get; set; }
    public int CalculateSomethingElse()
    {
        return IntProp * 23;
    }
}

所以现在编译好了:

DerivedLike dl = new DerivedLike();
Derived d = (Derived)dl;

回到内存主题,请注意,通过这种转换,您现在将在堆上有两个对象。

在这里创建一个:

DerivedLike dl = new DerivedLike();

第二个在这里创建:

Derived d = (Derived)dl;

堆上的对象无法更改其类型。

希望这澄清。