代码之家  ›  专栏  ›  技术社区  ›  Nick

将sbyte[]强制转换为bool[],将char[]强制转换为short[]

  •  12
  • Nick  · 技术社区  · 6 年前

    是否有明确的强制/强制

    • sbyte[] byte[] 到A bool[]
    • char[] 到A short[] / ushort[]

    在CIL,你经常看到

    stelem Type sbyte (ldloc pArray) ldc_i4 1 ldc_i4 0 
    

    在做什么 pArray[1] = true 在哪里? pArray 是类型为的一维数组 布尔[ ] . 我想在C中复制这个

    (sbyte[])pArray[1] = 1;
    

    不幸的是,C编译器不允许这样做。

    5 回复  |  直到 6 年前
        1
  •  17
  •   xanatos    6 年前

    无证戏法,自担风险:

    (如图所示 here 在许多其他地方)

    [StructLayout(LayoutKind.Explicit)]
    public struct ConvSByteBool
    {
        [FieldOffset(0)]
        public sbyte[] In;
        [FieldOffset(0)]
        public bool[] Out;
    }
    

    然后:

    var bytes = new sbyte[] { -2, -1, 0, 1, 2 };
    var conv = new ConvSByteBool { In = bytes }.Out;
    bool b1 = conv[0]; // true
    bool b2 = conv[1]; // true
    bool b3 = conv[2]; // false
    bool b4 = conv[3]; // true
    bool b5 = conv[4]; // true
    

    请注意,这个技巧与泛型完全不兼容。不 Conv<T, U> !

    当源和目标中元素的大小相同时,该技巧最有效。( sizeof(sbyte) == sizeof(bool) )否则会有一些限制(在上面的链接问题中描述)。

        2
  •  9
  •   Matthew Watson    6 年前

    你可以用 the new Span<T> and MemoryMarshal types 这样做。

    请注意,这只适用于C_的最新版本,此时您必须使用nuget包来提供库,但这会有所改变。

    例如,要将char数组“强制转换”为短数组,可以编写如下代码:

    var         charArray  = new char[100];
    Span<short> shortArray = MemoryMarshal.Cast<char, short>(charArray);
    
    charArray[0] = 'X';
    Console.WriteLine(charArray[0]); // Prints 'X'
    ++shortArray[0];
    Console.WriteLine(charArray[0]); // Prints 'Y'
    

    这种方法是有文档记录的,不会对任何数据进行任何复制,而且它(按设计)的性能也非常好。

    请注意,这也适用于结构:

    struct Test
    {
        public int X;
        public int Y;
    
        public override string ToString()
        {
            return $"X={X}, Y={Y}";
        }
    }
    
    ...
    
    var testArray = new Test[100];
    Span<long> longArray = MemoryMarshal.Cast<Test, long>(testArray);
    
    testArray[0].X = 1;
    testArray[0].Y = 2;
    
    Console.WriteLine(testArray[0]); // Prints X=1, Y=2
    
    longArray[0] = 0x0000000300000004;
    
    Console.WriteLine(testArray[0]); // Prints X=4, Y=3
    

    还要注意,这允许您做一些可疑的事情,如:

    struct Test1
    {
        public int X;
        public int Y;
    
        public override string ToString()
        {
            return $"X={X}, Y={Y}";
        }
    }
    
    struct Test2
    {
        public int X;
        public int Y;
        public int Z;
    
        public override string ToString()
        {
            return $"X={X}, Y={Y}, Z={Z}";
        }
    }
    
    ...
    
    var         test1 = new Test1[100];
    Span<Test2> test2 = MemoryMarshal.Cast<Test1, Test2>(test1);
    
    test1[1].X = 1;
    test1[1].Y = 2;
    
    Console.WriteLine(test1[1]); // Prints X=1, Y=2
    
    test2[0].Z = 10; // Actually sets test1[1].X.
    
    Console.WriteLine(test1[1]); // Prints X=10, Y=2
    
        3
  •  1
  •   Jeppe Stig Nielsen    6 年前

    这只是部分答案。

    乱劈:

    C编译器和运行时在哪些数组类型可以相互转换(正如您在问题中暗示的那样)上并不完全一致。因此,您可以通过遍历 System.Array (或) System.Object System.Collections.IEnumerable 等等)。

    一个例子:

    using System;
    
    static class P
    {
      static void Main()
      {
        var a = new sbyte[] { -7, -3, 8, 11, };
        var hack = (byte[])(Array)a;
        Console.WriteLine(string.Join("\r\n", hack));
      }
    }
    

    Try it online (tio.run)

    输出:

    249
    253
    8
    11

    如果你要避开中间人 (Array) 在上面的代码中,C编译器会告诉您强制转换是不可能的。

        4
  •  0
  •   AustinWBryan ravenspoint    6 年前

    您可以使用扩展方法,如下所示:

    namespace ExtensionMethods
    {
        public static class ByteExt
        {
            public static bool ToBool(this byte b) => b != 0;
            public static bool[] ToBoolArray(this byte[] bytes)
            {
                bool[] returnValues = new bool[bytes.Length];
    
                for (int i = 0; i < bytes.Length; i++)
                    returnValues[i] = bytes[i].ToBool();
    
                return returnValues;
            }
    
            // Do same for sbyte
        }
        public static class CharExt
        {
            public static short[] ToBoolArray(this char[] chars)
            {
                short[] returnValues = new bool[chars.Length];
    
                for (int i = 0; i < chars.Length; i++)
                    returnValues[0] = (short)chars[0];
    
                return returnValues;
            }
        }
    }
    

    然后就这样使用它:

    byte[] myBytes = new[] {1, 2, 5};
    bool[] myBools = myBytes.ToBoolArray();
    

    在C 8中,可能会有所谓的“扩展一切”,在这里您可以定义自己的扩展强制转换,包括显式和隐式。

    语法如下:

    public extension class ByteExt extends Byte[]
    {
        public static explicit operator bool[](byte[] bytes)
        {
             // Same implementation as before
        }
    }
    

    可以这样使用:

    byte[] myBytes = new[] {1, 2, 5};
    bool[] myBools = (bool[])myBytes;
    
        5
  •  -1
  •   Hossein Golshani    6 年前

    使用 Array.ConvertAll 如下:

    // Input
    sbyte[] sbyteArray = { 0, 1, 2, 0 };
    byte[] byteArray = { 0, 1, 2, 0 };
    // Result
    bool[] boolArray1 = Array.ConvertAll(sbyteArray, (item) => Convert.ToBoolean(item));
    bool[] boolArray2 = Array.ConvertAll(byteArray, (item) => Convert.ToBoolean(item));
    
    // Input
    char[] charArray = { 'A', 'B', 'C' };
    // Result
    short[] shortArray = Array.ConvertAll(charArray, (item) => Convert.ToInt16(item));
    ushort[] ushortArray = Array.ConvertAll(charArray, (item) => Convert.ToUInt16(item));