如何在不装箱的情况下存储不同类型的结构 [英] How to store structs of different types without boxing

查看:141
本文介绍了如何在不装箱的情况下存储不同类型的结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个用于XNA游戏的消息传递系统.我的消息类型是结构,因为我希望它们以值类型"的方式运行.

I'm creating a messaging system for use in an XNA game. My Message types are structs because I want them to behave in a Value Type way.

struct MyMessageType1 : IMessage {}
struct MyMessageType2 : IMessage {}

List<IMessage> messageQueue = new List<IMessage>();

我希望能够将不同类型的消息存储在我的消息队列中,但是我希望不将它们中的任何一个都装箱.

I want to be able to store Messages of different types in my message queue, but I want to do so without any of them being boxed.

如果我让这些结构实现IMessage之类的接口,并且尝试将它们存储在List中,则会将它们装箱.

If I have the structs implement an interface such as IMessage and I try to store them in a List, they get boxed.

我不知道所有可能的消息类型,所以我不能仅对每种类型的一个列表进行硬编码.

I don't know all the possible message types ahead of time, so I can't just hard code one List for each type.

所以问题是,如何在不装箱的情况下存储不同类型的结构的列表?

So the question is how can I store a list of structs of different types without them being boxed?

推荐答案

此操作无法完成.

但是,您可以使用两个列表(List<MyMessageType1>List<MyMessageType2>)来模拟事物.

However, you can emulate things, by using two Lists (List<MyMessageType1> and List<MyMessageType2>).

然后,您可以编造一个超级索引(可能只是另一个整数(长整数?)数组),从而可以(间接)对某项进行寻址,就好像它是一个列表一样.

You then concoct one Super Index (possibly, just another array of ints (longs?)) to make it possible to (indirectly) address an item as if it were one list.

您可能想优化索引(游程编码:仅将索引存储在支持数组切换的位置:这在迭代已知在其中一个支持数组中连续的子范围时也将有很大帮助)

You might want to optimize the index (runlength encoding: store just the indexes where the backing array switches: this will also enormously help when iterating a subrange that is known to be contiguous in one of the backing arrays)

列表内部使用数组存储,因此 -你没有拳击 -快速随机访问 -使用list.ForEach

Lists use Array storage internally, so - you get no boxing - fast random access - blazing iteration with list.ForEach

查看StructLayout属性,并通过执行所有操作以某种方式模拟Union.如果您确实准备好动手操作,请放入unsafe {}块(并使用/unsafe进行编译)...但是,如果重要,请认真考虑P/调用C DLL或使用C ++/CLI 很多

Look at the StructLayout attribute and somehow emulate a Union by doing all the manipulations. If you are really prepared to get your hands dirty, throw in unsafe {} blocks (and compile with /unsafe) ... however, seriously consider P/Invoke a C DLL or use C++/CLI if it matters that much

因为我真的很喜欢Marc Gravell指出您可以使用我提到的StructLayout,以便将 union .NET结构的所有三个成员都定位在相同的偏移量;我以为我会采取额外的措施,看看我是否还能使 leaky transparent 陷入困境.这几乎是透明的:

Because I really liked the fact that Marc Gravell pointed out you can use the StructLayout that I mentioned, to pinpoint all three members of a union .NET struct at the same offset; I thought I'd go the extra step and see whether I could make that a hell of a lot more leaky tranparent still. This comes pretty close to being transparent:

using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace LeakyAbstractions
{
    struct TypeA {}
    struct TypeB {}
    struct TypeC {}

    [StructLayout(LayoutKind.Explicit)] internal struct AnyMessage {
        [FieldOffset(0)] public TypeA A;
        [FieldOffset(0)] public TypeB B;
        [FieldOffset(0)] public TypeC C;

        AnyMessage(TypeA a) { A = a; }
        AnyMessage(TypeB b) { B = b; }
        AnyMessage(TypeC c) { C = c; }

        public static implicit operator TypeA(AnyMessage msg) { return msg.A; }
        public static implicit operator TypeB(AnyMessage msg) { return msg.B; }
        public static implicit operator TypeC(AnyMessage msg) { return msg.C; }

        public static implicit operator AnyMessage(TypeA a) { return a; }
        public static implicit operator AnyMessage(TypeB b) { return b; }
        public static implicit operator AnyMessage(TypeC c) { return c; }
    }

    public class X
    {
        public static void Main(string[] s) 
        {
            var anyMessages = new List<AnyMessage> { 
                new TypeA(),
                new TypeB(),
                new TypeC(),
            };

            TypeA a = anyMessages[0];
            TypeB b = anyMessages[1];
            TypeC c = anyMessages[2];

            anyMessages.Add(a);
            anyMessages.Add(b);
            anyMessages.Add(c);
        }
    }
}

我会把区分这种可怜的男人的变种的问题留给您练习.简化列表的方法是在AnyMessage结构中添加一个字段,但是根据有效负载,其他策略可能在效率(时空)上更为有效.

I'll leave the problem of discriminating this poor men's variant as an exercise to you. The simplist way would be to add a field to the AnyMessage struct, but depending on the payload, other strategies might be much more (space/time) efficient.

我的$ 0.02

哦,我实际上从不这样做,因为它看起来太复杂了.我假设您有充分的理由对此进行优化

Oh, I'd never actually do this, because it seems like overcomplicated. I'm assuming you have a valid reason to optimize this

PS.如果您在此处阅读我的答案后才问这个问题(昨天:

PS. If you are asking this after reading my answer here (yesterday: Should I use a struct or a class to represent a Lat/Lng coordinate?), I'm going to snap-judge this premature optimization

这篇关于如何在不装箱的情况下存储不同类型的结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆