13

我想知道reinterpret_castC# 中的 C++ 的等价物是什么!?

这是我的示例:

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将给定的参数转换为预期的参数并调用服务。

4

7 回答 7

12

这行得通。是的,它和你想象的一样邪恶和可怕。

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[]to and U[]

  • 如果T大于U,则边界检查将阻止您访问U超出原始长度的元素T[]
  • 如果T小于U,则边界检查将让您读取最后一个元素(实际上是缓冲区溢出漏洞)
于 2017-02-06T23:09:40.900 回答
8

讨论

正如一些答案所指出的那样,.Net 在问题范围内严格执行类型安全。Areinterpret_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 框架中使用的模式。如果您仍然坚持使用类似的东西,这里有一个使用紧凑序列化库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绝对可以通过此序列化解决方案或直接通过反射来实现,但我目前不会投入时间。

于 2013-10-22T09:06:34.873 回答
3

您可能能够使用unsafe块和void*在 C# 中实现类似的行为:

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 管理的,所以你不能有指向它们的指针。

于 2015-08-07T21:46:39.890 回答
1

C# 在类型系统中没有允许您执行此操作的漏洞。它知道事物是什么类型,并且不允许您转换为不同的类型。原因很明显。向 Foo 添加字段时会发生什么?

如果你想要一个 Foo 类型,你需要创建一个 Foo 类型。更好的方法是创建一个以 Base 作为参数的 Foo 类型的构造函数。

于 2013-10-21T14:58:01.387 回答
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));
    }

确保在调用它时知道自己在做什么,尤其是引用类型。

于 2016-05-04T00:58:22.377 回答
1

如果 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;            
    }

但我不确定这是否适用于对象。

于 2017-11-09T09:48:52.343 回答
0

由于 b 只是 Base 的一个实例,因此您永远无法将其强制转换为 Foo 的非 null 实例。也许界面可能更适合您的需求?

于 2013-10-21T14:53:48.583 回答