C# 声明不安全的固定自定义结构数组的变通方法?

C# 声明不安全的固定自定义结构数组的变通方法?,c#,pointers,struct,fixed-size-types,C#,Pointers,Struct,Fixed Size Types,对于错误CS1663(“固定大小的缓冲区类型必须是以下类型之一:bool、byte、short、int、long、char、sbyte、ushort、uint、ulong、float或double”) 我需要从另一个blittable自定义类型结构声明一个不安全的固定数组,但我陷入了这个编译器错误 显示一些代码来说明下面的问题 struct s1 { byte _b1; byte _b2; } unsafe struct s2 { fixed s1 _s1[5]; //

对于错误CS1663(“固定大小的缓冲区类型必须是以下类型之一:bool、byte、short、int、long、char、sbyte、ushort、uint、ulong、float或double”)

我需要从另一个blittable自定义类型结构声明一个不安全的固定数组,但我陷入了这个编译器错误

显示一些代码来说明下面的问题

struct s1
{
    byte _b1;
    byte _b2;
}

unsafe struct s2
{
    fixed s1 _s1[5]; // CS1663 here...
}
请注意,这两个结构是可blittable的,所以这个错误对我来说没有任何意义

有人知道我能做什么吗

谢谢

这是一种限制

固定数组可以采用以下任意属性或修改器: 允许常规结构成员使用。唯一的限制是 数组类型必须是bool字节字符整数longsbyteushortuintulongfloatdouble

只能使用该类型,不能使用组合(如
struct
仅包含该类型)。类型是否为bittable没有区别。你就是不能用它

然后,您不能将自定义的
结构
用于固定大小的缓冲区

解决办法?嗯,是的,可能是。您可以更改代码结构并使用以下内容:

unsafe struct s2
{
    fixed byte _b1[5]; 
    fixed byte _b2[5]; 
}

另一种解决方法是使用指针类型字段

public struct Foo
{
    public byte _b1;
    public byte _b2;
}

public unsafe struct Bar
{
    public int Length;
    public unsafe Foo* Data;
}
或者,如果您的意图是创建一个包含其自身所有数据的单值类型对象(即,可以在非托管代码中使用memcpy),那么唯一的方法就是根本不使用任何数组

public struct Foo
{
    public byte _b1;
    public byte _b2;
}

public struct Bar1
{
    public Foo F1;
}

public struct Bar2
{
    public Foo F1;
    public Foo F2;
}

public struct Bar3
{
    public Foo F1;
    public Foo F2;
    public Foo F3;
}

我将如何处理您的示例和类似的复杂示例,首先要问我自己是否可以将结构简化为单个基本类型。如果可以的话,我只会通过属性或索引器访问将固定缓冲区隐藏在结构内部

在您的示例中,您有以下内容

struct s1
{
    byte _b1;
    byte _b2;
}

unsafe struct s2
{
    fixed s1 _s1[5]; // CS1663 here...
}
如果我决定绝对需要在一个连续的数据块中排列这些结构,我会考虑什么。
[StructLayout(LayoutKind.Explicit, Size = 2)]
struct s1
{   // Field offsets to emulate union style access which makes it
    // simple to get at the raw data in a primitive type format.

    [FieldOffset(0)]ushort _u1;
    [FieldOffset(0)]byte _b1;
    [FieldOffset(1)]byte _b2;

    public s1(ushort data)
    {
        _b1 = 0;
        _b2 = 0;
        _u1 = data;
    }

    public ushort ToUShort()
    {
        return _u1;
    }
}

unsafe struct s2
{
    public const int Size = 5;
    private fixed ushort _s1[Size];

    public s1 this[int index]
    {   // A public indexer that provides the data in a friendlier format.
        get
        {
            if (index < 0 || index >= Size )
                throw new IndexOutOfRangeException();
            return new s1(_s1[index]);
        }
        set
        {
            if (index < 0 || index >= Size)
                throw new IndexOutOfRangeException();
            _s1[index] = value.ToUShort();
        }
    }
}
[StructLayout(LayoutKind.Explicit,Size=2)]
结构s1
{//字段偏移量,以模拟使其
//以基本类型格式获取原始数据非常简单。
[FieldOffset(0)]ushort_u1;
[FieldOffset(0)]字节_b1;
[FieldOffset(1)]字节b2;
公共s1(ushort数据)
{
_b1=0;
_b2=0;
_u1=数据;
}
公共卫生服务
{
返回u1;
}
}
不安全结构s2
{
公共const int Size=5;
私人固定ushort_s1[尺寸];
公共s1此[int索引]
{//以更友好的格式提供数据的公共索引器。
得到
{
如果(索引<0 | |索引>=大小)
抛出新的IndexOutOfRangeException();
返回新的s1(_s1[索引]);
}
设置
{
如果(索引<0 | |索引>=大小)
抛出新的IndexOutOfRangeException();
_s1[索引]=value.ToUShort();
}
}
}

如果这看起来像黑客,那是因为它有点像黑客。我不建议将其作为一种通用解决方案,因为它很难维护,但在很少的情况下,如果您在这种低级别上工作,并且事先知道数据规范不会改变,那么类似这种技术是可行的。但是,即使在这些情况下,我仍然希望尽可能多地封装这些低级的东西,以尽量减少出错的可能性。这就是说,这完全符合要求,这是一个解决办法,有一个固定大小的自定义结构缓冲区。

你的意思是不安全的
structs2{fixeds1s1[5];}
?是的!!!我将在代码片段中修复这个小错误,谢谢。对这个@VadimMartynov有什么想法吗?嗯,你只是有一个语法错误。您应该声明字段修饰符(如private、fixed、readonly、static和other)、字段类型(如byte、int、s1、string)和字段名称(用于引用变量的任何名称)。您缺少字段类型(s1)。请阅读有关声明固定缓冲区的更多信息:@VadimMartynov我知道我有一个语法错误:D我的问题围绕“为什么”我遇到了这个语法错误。我能想象出这个错误的唯一原因是为了避免在内存中修复一个不可blittable的类型。问题是我的类型实际上是可blittable的。对于这个限制,必须有一些解决方法,这应该是一个非常常见的问题。回复我上面的评论:@瓦迪马蒂诺夫我知道我有一个语法错误:D我的问题围绕着“为什么”我遇到了这个语法错误。我能想象出这个错误的唯一原因是为了避免在内存中修复一个不可blittable的类型。问题是我的类型实际上是可blittable的。对于这个限制,必须有一些解决方法,这应该是一个非常常见的问题。"您的解决方案与我的示例不匹配!在您的示例中,我将得到一个线性原始缓冲区,其中5个元素表示_b1,5个元素表示_b2。在我的示例中,内存中的缓冲区大小应该相同,但组织方式不同:首先是_b1的1个元素,然后是_b2的1个元素,依此类推。这就是此解决方案调用我知道:)我只是说这个变通方法不适合我的需要。就像我需要10个苹果和10个香蕉作为变通方法一样,就是不适合。如果在这方面没有任何合适的解决方法,我将别无选择,只能修改代码以使用封送处理样式:(((这太令人伤心了。。。
[StructLayout(LayoutKind.Sequential)]
public struct S1
{
    [MarshalAs(UnmanagedType.I1)] public byte _b1; 
    [MarshalAs(UnmanagedType.I1)] public byte _b2; 

    public S1(ushort _ushort)
    {
        this = new Converter(_ushort).s1;
    }
    public ushort USHORT() //for fixed arrays
    {
        return new Converter(this).USHORT;
    }

    [StructLayout(LayoutKind.Explicit)]
    private struct Converter
    {
        [FieldOffset(0)] public S1 s1;
        [FieldOffset(0)] public ushort USHORT;

        public Converter(S1 _s1)
        {
            USHORT = 0;
            s1 = _s1;
        }

        public Converter(ushort _USHORT)
        {
            s1 = default;
            USHORT = _USHORT;
        }
    }
}

public unsafe struct S2
{
    public fixed ushort s1[5];

    public S1 this[int n] {
        get => new S1(s1[n]); //copy!
        set => s1[n] = value.USHORT();
    }
}