我想知道在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<Foo>(b);
并得到我想要的东西。我能做些什么来在C#中实现同样的目标?
PS。我假设Base
和Foo
数据一致。
[UPDATE]
以下是reinterpret_cast
可能有用的简单方案:
考虑编写一个XXX-RPC库,您无法控制传入参数,也无法控制要调用的服务的签名。您的库应该使用给定的参数调用所请求的服务。如果支持C#reinterpret_cast
,我可以将reinterpret_cast
给定参数放入预期参数并调用服务。
答案 0 :(得分:6)
这很有效。是的,它像你可能想象的那样邪恶的和非常棒。
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
,则边界检查会让您读取最后一个元素(实际上是缓冲区溢出漏洞)答案 1 :(得分:5)
正如一些答案所指出的那样,.Net在问题的范围内严格执行类型安全。 reinterpret_cast
本身就是一种不安全的操作,因此实现它的可能方法是通过 reflection 或序列化,而这两者是相关的。
正如您在更新中提到的,可能的用途可能是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
如果您不想在合同中声明派生类型,请参阅this example...
如您所见,序列化非常紧凑。
如果要使用更多字段,可以尝试隐式序列化字段:
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
通常可以通过此序列化解决方案或直接通过反射来实现通用reinterpret_cast
,但我现在不会投入时间。
答案 2 :(得分:2)
您可以在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管理的,所以你不能指向它们。
答案 3 :(得分:1)
C#在类型系统中没有允许你这样做的漏洞。它知道什么类型的东西,不允许你转换为不同的类型。原因很明显。向Foo添加字段时会发生什么?
如果你想要一种Foo,你需要创建一种Foo。可能是更好的路线是创建一个Foo类型的构造函数,它将Base作为参数。
答案 4 :(得分:1)
这是我的'实施'
[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));
}
确保在调用时知道自己在做什么,特别是使用引用类型。
答案 5 :(得分:0)
由于b只是Base的一个实例,因此永远无法将其强制转换为非空的Foo实例。也许界面可能更适合您的需求?
答案 6 :(得分:0)
如果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;
}
但我不确定这会对物体起作用。