在 C# 中引用类型的内存方面的显式转换解释
Posted
技术标签:
【中文标题】在 C# 中引用类型的内存方面的显式转换解释【英文标题】:Explicit cast explanation in terms of memory for reference type in C# 【发布时间】:2016-10-28 04:57:45 【问题描述】:在 MSDN 中,“对于引用类型,如果您需要从基本类型转换为派生类型,则需要显式转换”。
在 wiki 中,“在编程语言理论中,引用类型是指引用内存中的对象的数据类型。另一方面,指针类型指的是内存地址。引用类型可以被认为是隐式取消引用的指针。”在 C 中就是这种情况。
在 C# 中考虑显式转换引用类型时如何解释内存存储过程?
【问题讨论】:
对象存储在内存中,独立于对对象的引用。转换用于获取将对象视为特定类型的引用。 From the same MSDN page: "引用类型之间的强制转换操作不会改变底层对象的运行时类型;它只会改变用作对该对象的引用的值的类型。" 问题讨论C#
,所以标签C
和C++
应该被删除
【参考方案1】:
在大多数情况下,引用变量和指针变量之间并没有太多可以想象的区别。两者都指向内存中的一个位置。引用(或指针)变量的类型告诉编译器可以使用它执行哪些操作。
首先考虑 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-Style 强制转换等同于 C++ 中的 try_cast,这将确保您永远不会将错误的目标实例分配给引用变量。
所以引用类型和指向对象的指针之间的区别是(其中大部分都隐含在 C# 是一种托管语言这一事实中):
引用变量永远不能指向无效内存(NULL 除外)。 引用变量永远不能指向不属于其类型的对象。 为引用变量赋值时,始终会测试类型。 对引用变量的强制转换需要检查目标对象是否属于给定类型。【讨论】:
【参考方案2】:引用对象存储在堆中,可以从代码中引用它们。对象,因为它在堆上,是给定类型的。
从代码中,您可以创建对它的引用,这些引用可以转换为其他一些类型。
现在,有几个案例,在参考文章中进行了描述。我将使用那里的示例使其更容易。
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
对象是安全的。因为Derived IS Base (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
类中实现一个转换运算符。它是一个静态运算符,它告诉如何将一种类型转换为另一种类型。转换运算符可以是隐式的,也可以是显式的。显式将要求开发人员通过在括号中提供类型名称来显式转换它。
在隐式和显式运算符之间进行选择的建议是,如果转换可能引发异常,则应该是显式的,以便开发人员有意识地进行转换。
让我们更改代码以满足该要求:
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;
堆上的对象不能改变它的类型。
希望这可以澄清。
【讨论】:
以上是关于在 C# 中引用类型的内存方面的显式转换解释的主要内容,如果未能解决你的问题,请参考以下文章