Приведение sbyte [] к bool [] и char [] к short [] - PullRequest
0 голосов
/ 23 мая 2018

Есть ли в любом случае явное приведение / принуждение

  • sbyte[] или byte[] к bool[]
  • char[] к short[] / ushort[]

В CIL вы регулярно видите что-то вроде

stelem Type sbyte (ldloc pArray) ldc_i4 1 ldc_i4 0 

, которое выполняет pArray[1] = true, где pArray - одномерный массив типа bool[].Я хочу повторить это в c #, выполнив

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

К сожалению, это не разрешено компилятором C #.

Ответы [ 5 ]

0 голосов
/ 23 мая 2018

Это только частичный ответ.

Hack:

Компилятор 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));
  }
}

Попробуйте онлайн (tio.run)

Вывод:

249
253
8
11

Если вы избегаете промежуточного (Array) в приведенном выше коде,Компилятор C # скажет вам, что приведение невозможно.

0 голосов
/ 23 мая 2018

Вы можете использовать новые Span<T> и MemoryMarshal типы для этого.

Обратите внимание, что это доступно только в последних версиях 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
0 голосов
/ 23 мая 2018

Вы можете использовать метод расширения, например:

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;
0 голосов
/ 23 мая 2018

Используйте 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));
0 голосов
/ 23 мая 2018

Недокументированный трюк, играйте на свой страх и риск:

(показано, например, здесь и во многих других местах)

[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)).В противном случае есть некоторые ограничения (описанные в связанном вопросе выше).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...