Испытывание затруднений из-за дополнительных методов для массивов байтов

Я работаю с устройством, которое передает изображение обратно, и когда я запрашиваю изображение, существует некоторая недокументированная информация, которая прибывает перед данными изображения. Я только смог понять это путем просмотра двоичных данных и идентификации информации заголовка изображения внутри.

Я первоначально имел нормальный метод и преобразовал его в дополнительный метод. Исходный вопрос здесь был связан с компилятором, жалующимся на не наличие Массива как первый параметр (у меня был Байт []), но оказывается, что я совершил ошибку и забыл удалять первый аргумент в коде вызова. Другими словами, я раньше имел:

Byte[] new_buffer = RemoveUpToByteArray(buffer, new byte[] { 0x42, 0x4D });

и после изменения на дополнительный метод, я ошибочно использовал:

buffer.RemoveUpToByteArray( buffer, new byte[] { 0x42, 0x4D });

Во всяком случае это все фиксируется теперь, потому что я понял свою ошибку, поскольку я вводил пример кода в ТАК. Однако у меня есть новая проблема, которая является просто отсутствием понимания дополнительных методов и ссылки по сравнению с типами значения. Вот код:

public static void RemoveFromByteArrayUntil(this Byte[] array, Byte[] until)
{
    Debug.Assert(until.Count() > 0);
    int num_header_bytes = until.Count();
    int header_start_pos = 0; // the position of the header bytes, defined by [until]
    byte first_header_byte = until[0];
    while(header_start_pos != -1) {
        header_start_pos = Array.IndexOf(array, first_header_byte, header_start_pos);
        if(header_start_pos == -1)
            break;
        // if we get here, then we've found the first header byte, and we need to look
        // for the next ones sequentially
        for(int header_ctr=1; header_ctr<num_header_bytes; header_ctr++) {
            // we're going to loop over each of the header bytes, but will
            // bail out of this loop if there isn't a match
            if(array[header_start_pos + header_ctr] != until[header_ctr]) {
                // no match, so bail out.  but before doing that, advance
                // header_start_pos so the outer loop won't find the same
                // occurrence of the first header byte over and over again
                header_start_pos++;
                break;
            }
        }
        // if we get here, we've found the header!
        // create a new byte array of the new size
        int new_size = array.Count() - header_start_pos;
        byte[] output_array = new byte[new_size];
        Array.Copy(array, header_start_pos, output_array, 0, new_size);
        // here is my problem -- I want to change what array points to, but
        // when this code returns, array goes back to its original value, which
        // leads me to believe that the first argument is passed by value.
        array = output_array;
        return;
    }
    // if we get here, we didn't find a header, so throw an exception
    throw new HeaderNotInByteArrayException();
}

Моя проблема теперь состоит в том, что это похоже на первое, этот аргумент дополнительному методу передается значением. Я хочу повторно присвоить то, на что указывает массив, но в этом случае, похоже, что я должен буду просто управлять данными массива вместо этого.

5
задан Dave 5 April 2010 в 14:35
поделиться

2 ответа

Методы расширения - это статические методы, которые только кажутся методами экземпляра. Вы можете считать, что экземпляр, над которым работает метод расширения, доступен только для чтения (по значению). Назначение метода экземпляра byte [], который является первым параметром вашего расширения, не будет работать. Вы не сможете уйти от назначения, но вы можете изменить свое расширение, а затем записать свое назначение следующим образом:

buffer = buffer.RemoveUpToByteArray(header);

Сделайте так, чтобы ваше расширение возвращало результат байтового массива, и не пытайтесь назначить его для буфера внутри расширения. Тогда ваше расширение будет примерно таким:

public static class MyExtensionMethods
{
    public static byte[] RemoveUpToByteArray(this byte[] buffer, byte[] header)
    {
        byte[] result = buffer;

        // your logic to remove header from result

        return result;
    }
}

Надеюсь, это поможет.

РЕДАКТИРОВАТЬ: Вышеуказанное верно только для типов значений. Если тип, который вы расширяете, является ссылочным типом, у вас не будет проблем, связанных непосредственно с этим типом, как вы пытаетесь сделать выше. К сожалению, байтовый массив представляет собой структуру и, следовательно, является производным от System.ValueType. Рассмотрим следующее, которое было бы совершенно допустимо внутри расширения и дало бы желаемый результат:

public class MyBytes
{
    public byte[] ByteArray { get; set; }
}

public static class MyExtensionMethods
{
    // Notice the void return here...
    public static void MyClassExtension(this MyBytes buffer, byte[] header)
    {
        buffer.ByteArray = header;
    }
}
5
ответ дан 14 December 2019 в 13:32
поделиться

Мне жаль, что я не знаю, с какой конкретной проблемой вы столкнулись, или как ее решить [конечно, проверка пространства имен и разрешение любых конфликтов с аналогично названными методами - это начало], но я заметил одну или две странности.

Рассмотрим следующий пример решения,

using System.Linq;
namespace Sample.Extensions
{
    public static class ByteExtensions
    {
        public static void RemoveHeader (this byte[] buffer, byte[] header)
        {
            // take first sequence of bytes, compare to header, if header
            // is present, return only content
            // 
            // NOTE: Take, SequenceEqual, and Skip are standard Linq extensions
            if (buffer.Take (header.Length).SequenceEqual (header))
            {
                buffer = buffer.Skip (header.Length).ToArray ();
            }
        }
    }
}

Он компилируется и запускается в VS2010RC. Чтобы продемонстрировать использование,

using Sample.Extensions;
namespace Sample
{
    class Program
    {
        static void Main (string[] args)
        {
            byte[] buffer = new byte[] { 00, 01, 02 };
            byte[] header = new byte[] { 00, 01 };
            buffer.RemoveHeader (header);

            // hm, so everything compiles and runs, but buffer == { 00, 01, 02 }
        }
    }
}

Итак, мы не получим ошибки компиляции или времени выполнения, но очевидно, что оно не будет работать так, как задумано. Это происходит потому, что расширения должны соответствовать стандартной семантике метода, то есть параметры передаются по значению. Мы не можем изменить buffer, чтобы он указывал на наш новый массив.

Мы можем решить эту проблему, переписав наш метод в соответствии с семантикой обычной функции,

public static byte[] RemoveHeaderFunction (this byte[] buffer, byte[] header)
{
    byte[] stripped = null;
    if (stripped.Take (header.Length).SequenceEqual (header))
    {
        stripped = stripped.Skip (header.Length).ToArray ();
    }
    else
    {
        stripped = buffer.ToArray ();
    }
    return stripped;
}

Сейчас

using Sample.Extensions;
namespace Sample
{
    class Program
    {
        static void Main (string[] args)
        {
            byte[] buffer = new byte[] { 00, 01, 02 };
            byte[] header = new byte[] { 00, 01 };

            // old way, buffer will still contain { 00, 01, 02 }
            buffer.RemoveHeader (header);

            // new way! as a function, we obtain new array of { 02 }
            byte[] stripped = buffer.RemoveHeaderFunction (header);
        }
    }
}

К сожалению, массивы являются неизменяемыми типами значений [возможно, вы неправильно используете эти термины]. Единственный способ изменить ваш "массив" на месте - это изменить контейнер на изменяемый ссылочный тип, например List.

Если вы действительно заинтересованы в семантике "передачи по ссылке", in-place, побочных эффектов, то одним из вариантов может быть следующий

using System.Linq;
namespace Sample.Extensions
{
    public static class ListExtensions
    {
        public static void RemoveHeader<T> (this List<T> list, List<T> header)
        {
            if (list.Take (header.Count).SequenceEqual (header))
            {
                list.RemoveRange (0, header.Count);
            }
        }
    }
}

Что касается использования,

static void Main (string[] args)
{
    byte[] buffer = new byte[] { 00, 01, 02 };
    byte[] header = new byte[] { 00, 01 };

    List<byte> bufferList = buffer.ToList ();

    // in-place side-effect header removal
    bufferList.RemoveHeader (header.ToList ());
}

Под капотом, List поддерживает массив типа T. На определенных пороговых значениях он просто манипулирует базовым массивом и/или создает новые массивы для нас.

Надеюсь, это поможет! :)

1
ответ дан 14 December 2019 в 13:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: