我想知道 C# 中 C++ 的
reinterpret_cast
相当于什么!?
这是我的样本:
class Base
{
protected int counter = 0;
}
class Foo : Base
{
public int Counter
{
get { return counter; }
}
}
Base b = new Base();
Foo f = b as Foo; // f will be null
我不反对为什么
f
将为空,因为它应该是空的。但如果是 C++,我可以编写 Foo f = reinterpret_cast<Foo>(b);
并得到我想要的东西。我该怎么做才能在 C# 中实现相同的目标?
PS。我假设
Base
和 Foo
在数据方面是一致的。
[更新]
这是一个简单的场景,其中
reinterpret_cast
可能会有所帮助:
考虑编写一个 XXX-RPC 库,您无法控制传入参数或要调用的服务的签名。您的图书馆应该使用给定的参数调用所请求的服务。如果 C# 支持
reinterpret_cast
,我可以简单地将 reinterpret_cast
给定参数转换为预期参数并调用服务。
这有效。是的,它就像你所能想象的那样邪恶并且那么可怕。
static unsafe TDest ReinterpretCast<TSource, TDest>(TSource source)
{
var sourceRef = __makeref(source);
var dest = default(TDest);
var destRef = __makeref(dest);
*(IntPtr*)&destRef = *(IntPtr*)&sourceRef;
return __refvalue(destRef, TDest);
}
需要注意的一件事是,如果您将
T[]
投射到 和 U[]
:
T
大于 U
,边界检查将阻止您访问超过 U
原始长度的
T[]
T
小于 U
,边界检查将允许您读取最后一个元素(实际上是缓冲区溢出漏洞)正如一些答案所指出的,.Net 在问题范围内严格执行类型安全。
reinterpret_cast
本质上是一种不安全的操作,因此实现该操作的可能方法是通过反射或序列化,而两者是相关的。
正如您在更新中提到的,可能的用途可能是 RPC 框架。无论如何,RPC 库通常使用序列化/反射,并且有几个可用的:
所以,你可能不想自己写一个。
如果您的类
Base
将使用公共属性,您可以使用AutoMapper:
class Base
{
public int Counter { get; set; }
// ...
}
...
AutoMapper.Mapper.CreateMap<Base, Foo>();
Foo foo = AutoMapper.Mapper.Map<Foo>(b);
其中
Foo
根本不需要从 Base
导出。它只需要具有您想要映射到的属性即可。但同样,您可能根本不需要两种类型 - 重新思考架构可能是解决方案。
通常,不需要使用
reinterpret_cast
,通过一个干净的架构,可以很好地适应 .Net Framework 中使用的模式。如果您仍然坚持拥有类似的东西,这里有一个使用紧凑序列化库protobuf-net的解决方案。
您的课程:
using System;
using System.IO;
using ProtoBuf;
using ProtoBuf.Meta;
[ProtoContract]
[ProtoInclude(3, typeof(Foo))]
class Base
{
[ProtoMember(1)]
protected int counter = 0;
public Base(int c) { counter = c; }
public Base() { }
}
[ProtoContract]
class Foo : Base
{
public int Counter { get { return counter; } }
}
以及一个可运行的序列化-反序列化示例:
class Program
{
static void Main(string[] args)
{
Base b = new Base(33);
using (MemoryStream stream = new MemoryStream())
{
Serializer.Serialize<Base>(stream, b);
Console.WriteLine("Length: {0}", stream.Length);
stream.Seek(0, SeekOrigin.Begin);
Foo f=new Foo();
RuntimeTypeModel.Default.Deserialize(stream, f, typeof(Foo));
Console.WriteLine("Foo: {0}", f.Counter);
}
}
}
输出
Length: 2
Foo: 33
如果您不想在合约中声明派生类型,请参阅此示例...
如您所见,序列化非常紧凑。
如果你想使用更多字段,你可以尝试字段的隐式序列化:
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
通用的
reinterpret_cast
肯定可以通过此序列化解决方案或直接通过反射来实现,但我目前不会投入时间。
您也许能够在 C# 中使用
unsafe
块和 void*
实现类似的行为:
unsafe static TResult ReinterpretCast<TOriginal, TResult>(this TOriginal original)
where TOriginal : struct
where TResult : struct
{
return *(TResult*)(void*)&original;
}
用途:
Bar b = new Bar();
Foo f = b.ReinterpretCast<Foo>();
f = ReinterpretCast<Foo>(b); // this works as well
未测试。
我想,结构约束使你的问题无效,但它们是必要的,因为类是由 GC 管理的,所以你不允许拥有指向它们的指针。
如果 Foo 和 Bar 是结构体,你可以这样做
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)]
public class MyFooBarHelper
{
[System.Runtime.InteropServices.FieldOffset(0)] public Foo theFoo;
[System.Runtime.InteropServices.FieldOffset(0)] public Bar theBar;
}
但我不确定这是否适用于物体。
C# 的类型系统中没有允许您执行此操作的漏洞。它知道事物是什么类型,并且不允许您转换为不同的类型。其原因相当明显。当您向 Foo 添加字段时会发生什么?
如果你想要一个 Foo 类型,你需要创建一个 Foo 类型。更好的方法可能是创建一个 Foo 类型的构造函数,它以 Base 作为参数。
这是我的“实现”
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public unsafe static TResult ReinterpretCast<TOriginal, TResult>(/*this*/ TOriginal orig)
//refember ReferenceTypes are references to the CLRHeader
//where TOriginal : struct
//where TResult : struct
{
return Read<TResult>(AddressOf(orig));
}
确保您在调用它时知道自己在做什么,尤其是引用类型。
由于 b 只是 Base 的一个实例,因此您永远无法将其转换为 Foo 的非 null 实例。也许界面可能更适合您的需求?
多年后,.NET 团队引入了
Unsafe.As<TFrom, TTo>
,基本上是 .NET 的重新诠释。确保您知道自己在做什么。
此 API 用于将对象转换为给定类型,从而抑制运行时的正常类型安全检查。调用者有责任确保转换是合法的。不会抛出 InvalidCastException。