Existe-t-il de toute façon explicitement cast / contraindre

  • sbyte[] ou byte[] à un bool[]
  • char[] à un short[] / ushort[]

Dans CIL, vous voyez régulièrement quelque chose comme

stelem Type sbyte (ldloc pArray) ldc_i4 1 ldc_i4 0 

Qui fait pArray[1] = truepArray est un tableau à une dimension de type bool[]. Je veux reproduire cela en c # en faisant

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

Malheureusement, cela n'est pas autorisé par le compilateur C #.

12
Nick 23 mai 2018 à 12:17

5 réponses

Meilleure réponse

Astuce non documentée, jouez à vos risques et périls:

(affiché par exemple ici et dans de nombreux autres endroits)

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

Puis:

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

Notez que cette astuce est totalement incompatible avec les génériques. Non Conv<T, U>!

L'astuce fonctionne mieux lorsque la taille de l'élément dans la source et la cible est la même (sizeof(sbyte) == sizeof(bool)). Sinon, il y a quelques limitations (décrites dans la question liée ci-dessus).

17
xanatos 23 mai 2018 à 09:33

Utilisez Array.ConvertAll comme suit:

// 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));
-1
Hossein Golshani 23 mai 2018 à 09:35

Vous pouvez utiliser les nouveaux Span<T> et MemoryMarshal types pour ce faire.

Notez que cela n'est disponible qu'avec les versions récentes de C # et que vous devez utiliser un package NuGet pour fournir la bibliothèque pour le moment, mais cela changera.

Ainsi, par exemple, pour "convertir" un tableau de caractères en un tableau court, vous pouvez écrire du code comme celui-ci:

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'

Cette approche est documentée et ne fait aucune copie des données - et elle est également extrêmement performante (de par sa conception).

Notez que cela fonctionne également avec les structures:

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

Notez également que cela vous permet de faire des choses suspectes, comme ceci:

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
9
Matthew Watson 23 mai 2018 à 10:01

Vous pouvez utiliser une méthode d'extension, comme ceci:

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

Ensuite, utilisez-le comme ceci:

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

En C # 8, il y aura probablement ce qu'on appelle "Extension Everything", où vous pourrez définir vos propres casts d'extension, à la fois explicites et implicites.

La syntaxe sera quelque chose comme ceci:

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

Et peut l'utiliser comme ceci:

byte[] myBytes = new[] {1, 2, 5};
bool[] myBools = (bool[])myBytes;
0
AustinWBryan 23 mai 2018 à 11:30

Il ne s'agit que d'une réponse partielle.

Pirater:

Le compilateur C # et le moteur d'exécution ne sont pas entièrement d'accord sur les types de tableaux convertibles les uns aux autres (comme vous le faites allusion dans votre question). Vous pouvez donc éviter de demander au compilateur et reporter le cast à l'exécution, en passant par System.Array (ou System.Object ou System.Collections.IEnumerable etc.).

Un exemple:

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

Essayez-le en ligne (to.run)

Production:

249
253
8
11

Si vous évitiez le (Array) intermédiaire dans le code ci-dessus, le compilateur C # vous dirait que le cast est impossible.

1
Jeppe Stig Nielsen 23 mai 2018 à 17:19