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

本文关键字:转换 解释 方面 引用类型 内存 | 更新日期: 2023-09-27 18:15:17

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

在wiki中,"在编程语言理论中,引用类型是指内存中对象的数据类型。另一方面,指针类型指内存地址。引用类型可以被认为是隐式取消引用的指针。"C.中就是这样

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

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

在大多数情况下,引用变量和指针变量之间实际上没有太大的区别。两者都指向内存中的某个位置。引用(或指针(变量的类型告诉编译程序可以使用它执行哪些操作

首先考虑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.值类型

如果您尝试强制转换的值类型的大小允许您将其存储在使您要强制转换为的类型的大小相同的内存中,那么编译器将允许您这样做。这主要是针对数值的,所以下面是你参考文章中的例子:

// 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所具有的所有成员。因此,转换引用并使用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.实现显式转换

只要不能自己从派生类转换为基类,在大多数其他情况下都可以编写运算符。

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

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

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

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;

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

希望这能澄清。