在C#中等效C ++的reinterpret_cast
我想知道在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
应该是null,因为它应该是。 但如果它是C ++我可以写Foo f = reinterpret_cast(b);
得到我想要的东西。 我能做些什么来在C#中实现同样的目标?
PS。 我假设Base
和Foo
在数据方面是一致的。
[UPDATE]
这是一个简单的场景,其中reinterpret_cast
可能会有所帮助:
考虑编写一个XXX-RPC库,您无法控制传入的参数,也无法控制要调用的服务的签名。 您的库应该使用给定的参数调用所请求的服务。 如果C#支持reinterpret_cast
我可以简单地将给定参数reinterpret_cast
为预期参数并调用服务。
讨论
正如一些答案所指出的那样,.Net在问题的范围内严格执行类型安全。 reinterpret_cast
将是一种固有的不安全操作,因此实现它的可能方式可能是通过reflection或序列化 ,而两者是相关的。
正如您在更新中提到的,可能的用途可能是RPC框架。 RPC库通常使用序列化/reflection,并且有几个可用的:
- protobuf的远程
- msgpack-RPC-CLI
所以,你可能不想自己写一个。
如果你的类Base
使用公共属性,你可以使用AutoMapper :
class Base { public int Counter { get; set; } // ... }
…
AutoMapper.Mapper.CreateMap (); Foo foo = AutoMapper.Mapper.Map(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 (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
可能肯定可以通过这个序列化解决方案实现,也可以直接通过reflection来实现,但我现在不会投入时间。
这很有效。 是的,它就像你可能想象的那样邪恶和令人敬畏。
static unsafe TDest ReinterpretCast(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
,则边界检查将阻止您访问超过T[]
原始长度的U
元素 - 如果
T
小于U
,则边界检查将允许您读取最后一个元素(实际上是缓冲区溢出漏洞)
您可能能够在C#中使用unsafe
blocks和void*
实现类似的行为:
unsafe static TResult ReinterpretCast(this TOriginal original) where TOriginal : struct where TResult : struct { return *(TResult*)(void*)&original; }
用法:
Bar b = new Bar(); Foo f = b.ReinterpretCast(); f = ReinterpretCast (b); // this works as well
未经测试。
我想,结构约束会使你的问题无效,但它们是必要的,因为类是由GC管理的,因此你不能指向它们。
C#在类型系统中没有允许你这样做的漏洞。 它知道什么类型的东西,不会允许你转换为不同的类型。 原因很明显。 向Foo添加字段时会发生什么?
如果你想要一种Foo,你需要创建一种Foo。 可能更好的路径是创建一个类型为Foo的构造函数,它将Base作为参数。
这是我的’实施’
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public unsafe static TResult ReinterpretCast(/*this*/ TOriginal orig) //refember ReferenceTypes are references to the CLRHeader //where TOriginal : struct //where TResult : struct { return Read(AddressOf(orig)); }
确保在调用时知道自己在做什么,尤其是引用类型。
由于b只是Base的一个实例,因此永远无法将其强制转换为Foo的非null实例。 也许界面可能更适合您的需求?
如果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; }
但我不确定这会对物体起作用。