声明不安全的固定自定义结构数组的解决方法?

问题描述 投票:0回答: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,因此该错误对我来说没有任何意义。

有人知道我能做什么吗?

谢谢。

c# pointers struct fixed-size-types
5个回答
2
投票

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

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

1
投票

这是对固定大小缓冲区的限制。

固定数组可以采用任何属性或修饰符 允许常规结构成员。唯一的限制是 数组类型必须是 boolbytecharshortintlongsbyteushortuintulongfloatdouble

您只能使用该类型,但不能使用组合(例如

struct
仅包含该类型)。无论您的类型是否为 bittable,都没有区别。你只是无法使用它。

那么你就不能使用自定义的

struct
作为固定大小的缓冲区。

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

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

0
投票

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

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

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();
        }
    }
}

如果这看起来像黑客攻击,那是因为它确实如此。我不会推荐将此作为通用解决方案,因为它很难维护,但在极少数情况下,您在这种低级别上工作并且提前知道数据规范不会改变,那么可以使用类似这种技术的东西可以是可行的。但是,即使在这些情况下,我仍然希望尽可能多地封装这些低级内容,以最大程度地减少出现问题的可能性。也就是说,这确实完全按照要求执行,这是一种解决方法,可以拥有固定大小的自定义结构缓冲区。


0
投票
[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();
    }
}

0
投票

C# 12 (Dotnet 8+) 的解决方案

如果您能够使用 C# 12 (dotnet 8),则可以使用

System.Runtime.CompilerServices.InlineArray
属性,它允许您创建一个结构体来表示另一个结构体的固定大小数组,而无需任何指针恶作剧。

看一下:

struct MyData
{
    byte _b1;
    byte _b2;
}

// We want our array to be 5 elements long, so specify a size of 5
[System.Runtime.CompilerServices.InlineArray(5)]
struct MyDataInlineArray
{
    // Note that structs with the InlineArray attribute must contain EXACTLY one member.
   MyData Element;
   // In this example, this struct is now a 5-long inline array of MyData. 
}

// Notice that this doesn't have to be marked unsafe anymore.
struct StructWithInlineArray
{
    MyDataInlineArray myDataArray;
}

然后您可以像普通数组一样使用

InlineArray
属性对类型进行索引:

public static void Main() {
    StructWithInlineArray foo = new();

    foo.myDataArray[0] = new MyData();
    foo.myDataArray[1] = ...;

    // You can also just use it directly
    MyDataInlineArray bar = new();
    bar[0] = ...;
}

您可以在此处阅读有关它们的更多信息。

© www.soinside.com 2019 - 2024. All rights reserved.