我正在使用C#处理大型值类型数组。我希望能够将兼容值类型的数组进行强制转换,例如:
struct Color
{
public byte R, G, B, A;
}
Color[] bitmap1 = ...;
uint[] bitmap2 = MagicCast(bitmap1);
我希望bitmap2能与bitmap1共享内存(它们具有相同的位表示)。我不想复制。是否有方法可以实现?
我正在使用C#处理大型值类型数组。我希望能够将兼容值类型的数组进行强制转换,例如:
struct Color
{
public byte R, G, B, A;
}
Color[] bitmap1 = ...;
uint[] bitmap2 = MagicCast(bitmap1);
public static class ColorExtension
{
public static uint[] GetUInts(this Color[] colors)
{
if(colors == null) throw new ArgumentNullException("colors");
Evil e = new Evil { Colors = colors};
return e.UInts;
}
[StructLayout(LayoutKind.Explicit)]
struct Evil
{
[FieldOffset(0)]
public Color[] Colors;
[FieldOffset(0)]
public uint[] UInts;
}
}
这基本上有两个数组指向相同的内存位置。
所以如果你想从颜色数组中获取无符号整数数组,可以按照以下方式进行:
Color[] colors = ...
uint[] uints = colors.GetUInts();
如评论中所提到的,您可能需要显式声明颜色结构,否则运行时可能会在顺序上出现问题导致一些“奇怪”的无符号整数值...
[StructLayout(LayoutKind.Sequential)] // Or explicit, but i bellieve Sequential is enough.
struct Color
{
public byte R;
public byte G;
public byte B;
public byte A;
}
是的,你可以这样做,但是根据发布的结构体,它可能只是偶然起作用。
你应该给你的结构体添加一些属性,以确保没有添加填充。
无论如何,这里有一个LINQPad程序来演示:
unsafe void Main()
{
Color[] c = new Color[2]
{
new Color { R = 255, G = 0, B = 0, A = 0 },
new Color { R = 0, G = 255, B = 0, A = 0 }
};
c.Dump();
fixed (byte* p = &c[0].R)
{
uint* i = (uint*)p;
*i = 0x11223344;
*(i + 1) = 0x55667788;
};
c.Dump();
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Color
{
public byte R, G, B, A;
}
你不是想把你的颜色对象看作C联合体吗?
这样做:
[StructLayout(LayoutKind.Explicit)]
struct Color
{
[FieldOffset(0)]
byte R;
[FieldOffset(1)]
byte G;
[FieldOffset(2)]
byte B;
[FieldOffset(3)]
byte A;
[FieldOffset(0)]
uint Value;
}
如果你想从一个Color[]获取IEnumerable而不需要复制,只需执行以下操作:
var converted = colorArray.Select( c => c.Value );
不应该制作任何副本,因为它应该只是将值投影到数组中。
你甚至可以更进一步,为你的新类型添加显式和隐式转换运算符。
struct Color
{
//...As before...
public static implicit operator uint(Color input)
{
return input.Value;
}
public static explicit operator uint(Color input)
{
return input.Value;
}
}
然后你就可以像下面这样做:
Color a = new Color();
uint b = a; //Completely valid, with the implicit cast.
uint c = (uint)a; //Also valid, but the implicit cast makes it unnecessary.
然而,这并不涵盖数组强制转换。
这应该可以做到你想要的,你改变一个,它会同时改变引用。
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
[StructLayout(LayoutKind.Explicit)]
struct Color
{
[FieldOffset(0)]
public uint value;
[FieldOffset(0)]
public byte R;
[FieldOffset(1)]
public byte G;
[FieldOffset(2)]
public byte B;
[FieldOffset(3)]
public byte A;
static public implicit operator uint(Color c)
{
return c.value;
}
}
class Program
{
static unsafe void Main(string[] args)
{
Color[] colors = new Color[] { new Color { R = 255 }, new Color { B = 255 } };
Console.WriteLine(colors[0]);
Console.WriteLine(colors[1]);
fixed (Color* thisPtr = &colors[0])
{
((uint*)thisPtr)[0] = 2;
((uint*)thisPtr)[1] = 4;
Console.WriteLine(colors[0]);
Console.WriteLine(colors[1]);
}
Console.ReadKey();
}
}
}
看起来你想要同一件事情的不同表示。4个字节的颜色或一个uint。这可以使用C语言中的union来完成。在C#中没有本地的union结构,但是可以通过StructLayout和FieldOffset属性重现功能。像这样:
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
struct Color
{
[FieldOffset(0)]
public byte R;
[FieldOffset(1)]
public byte G;
[FieldOffset(2)]
public byte B;
[FieldOffset(3)]
public byte A;
[FieldOffset(0)]
public uint value;
}
var allRedBytes = bitmap1.Select(c => c.R);
var allUInts = bitmap1.Select(c => c.value);
试试这个:
unit[] bitmap2 = bitmap1.Select((c)=>c.ToUint32()).ToArray();
你可能需要创建一个扩展方法来将Color
转换为uint
public static uint ToUint32(this Color color) { ... }
Color
是一个 long
而不是 uint
。因此,您可以获取指向私有的 Color.value
字段的指针。 - John Alexiou