Casting Arrays in C #

In C #, I work with large arrays of value types. I want to be able to create arrays of compatible value types, for example:

struct Color { public byte R, G, B, A; } Color[] bitmap1 = ...; uint[] bitmap2 = MagicCast(bitmap1); 

I want bitmap2 to share memory with bitmap1 (they have the same bit representation). I do not want to make a copy.

Is there any way to do this?

+6
source share
6 answers

You can get this using Structlayouts to overlay both arrays. Although many of the answers here wrote on how to make color-struct also uint, this is about transforming the "array":

This evil.

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

Basically these are two arrays referring to the same memory location.

So, if you want to get an uints array from an array of colors, do this:

 Color[] colors = ... uint[] uints = colors.GetUInts(); 

As mentioned in the comments, you may also need to explicitly declare colorstruct, or the runtime may be distorted using an order leading to some “weird” uint values ​​...

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

Do not try to debug it though ... One of the reasons why this is evil:

+4
source

Yes, you can do this, but with a structure published, it can work by accident.

You should definitely add some attributes to your structure so as not to add add-ons.

Anyway, here's a LINQPad program that demonstrates:

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

This will result in two colors, twice, where the second time it has been changed.

+1
source

Aren't you trying to treat your Color object as a C connection?

Do it:

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

To get your IEnumerable from Color [] without creating a copy, simply do:

 var converted = colorArray.Select( c => c.Value ); 

No need to make any copies, I would not have thought, since this should be just a projection of the values ​​into an array.

Perhaps you may even take a step further and add an explicit and implicit distribution operator for your new type.

 struct Color { //...As before... public static implicit operator uint(Color input) { return input.Value; } public static explicit operator uint(Color input) { return input.Value; } } 

Then you can do something like the following:

 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. 

However, this does not apply to array casting.

0
source

it should do what you want, you change it, it changes as a link

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

Try the following:

 unit[] bitmap2 = bitmap1.Select((c)=>c.ToUint32()).ToArray(); 

You may need to create an extension method to convert Color to uint

 public static uint ToUint32(this Color color) { ... } 
-1
source

You seem to want a different view of the same. The color is 4 bytes or one uint. This is done using unions in C. C # does not have a built-in union construct, but functionality can be reproduced using the StructLayout and FieldOffset attributes. Something like that:

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

Then you can use bitmap1 of the color array to select what you need, for example:

 var allRedBytes = bitmap1.Select(c => cR); var allUInts = bitmap1.Select(c => c.value); 

Until you call .ToArray or .ToList in LINQ queries, you do not create a new copy of the raster image array. These queries describe only how to get through the array when you use the query in a foreach loop, for example.

-1
source

Source: https://habr.com/ru/post/973229/


All Articles