字节数组的扩展方法遇到问题

发布于 2024-08-27 12:40:36 字数 2493 浏览 5 评论 0原文

我正在使用发送回图像的设备,当我请求图像时,图像数据之前会出现一些未记录的信息。我只能通过查看二进制数据并识别其中的图像标题信息来实现这一点。

我原本有一个普通的方法,并将其转换为扩展方法。这里最初的问题与编译器抱怨没有将 Array 作为第一个参数(我有 Byte[])有关,但事实证明我犯了一个错误,忘记删除调用代码中的第一个参数。换句话说,我曾经有:

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

在更改为扩展方法之后,我错误地使用了:

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

无论如何,现在都已修复,因为当我将代码示例输入到 SO 中时,我意识到了我的错误。 但是,我有一个新问题,就是缺乏对扩展方法以及引用与值类型的理解。这是代码:

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

我现在的问题是,扩展方法的第一个 this 参数看起来是按值传递的。我想重新分配数组指向的内容,但在这种情况下,看起来我必须只操作数组的数据。

I'm working with a device that sends back an image, and when I request an image, there is some undocumented information that comes before the image data. I was only able to realize this by looking through the binary data and identifying the image header information inside.

I originally had a normal method and converted it to an extension method. The original question here was related to the compiler complaining about not having Array as the first parameter (I had Byte[]), but it turns out that I had made an error and forgot to delete the first argument in the calling code. In other words, I used to have:

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

and after changing to an extension method, I had erroneously used:

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

Anyhow, that's all fixed now because I realized my mistake as I was entering the code example into SO. However, I have a new problem that is simply lack of understanding of extension methods and reference vs. value types. Here's the code:

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

My problem now is that it looks like the first this argument to the extension method is passed by value. I want to reassign what array points to, but in this case, it looks like I'll have to just manipulate array's data instead.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

雨轻弹 2024-09-03 12:40:36

扩展方法是静态方法,仅看起来是实例方法。您可以将扩展方法正在处理的实例视为只读(按值)。分配给作为扩展的第一个参数的 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;
    }
}

Extension methods are static methods that only appear to be instance methods. You can consider the instance the extension method is working on to be read only (by value). Assigning to the instance method of byte[] that is the first parameter of your extension won't work. You won't be able to get away from assigning, but you could modify your extension then write your assignment like this:

buffer = buffer.RemoveUpToByteArray(header);

Make your extension return the byte array result, and don't try to assign to buffer within the extension. Your extension would then be something like this:

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

I hope this helps.

EDIT:
The above is correct for value types only. If the type you are extending is a reference type, then you would not have an issue operating directly on the type like you are trying to do above. Sadly, a byte array is a struct, and thus derived from System.ValueType. Consider the following, which would be perfectly legal inside an extension, and would give the desired result:

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;
    }
}
极致的悲 2024-09-03 12:40:36

很抱歉,我不知道您遇到了什么具体问题,也不知道如何解决它[当然,检查引用的命名空间并解决与类似命名方法的任何冲突是一个开始],但我确实注意到一两个奇怪的地方。

考虑以下示例解决方案,

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

如果您真的热衷于“通过引用传递”、就地、副作用语义,那么一个选项可能是以下内容

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 类型的数组。在某些阈值下,它只是简单地操作底层数组和/或为我们实例化新数组。

希望这有帮助! :)

I am sorry that I do not know what specific problem you are encountering, or how to resolve it [certainly checking namespace is referenced and resolving any conflicts with similarly named methods is a start], but I did notice one or two oddities.

Consider the following sample solution,

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

This compiles and runs in VS2010RC. To demonstrate usage,

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

So we will not receive a compile or run-time error but clearly it will not operate as intended. This is because extensions must still comply with standard method semantics, meaning parameters are passed by value. We cannot change buffer to point to our new array.

We can resolve this issue by rewriting our method to conventional function semantics,

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

Now

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

Unfortunately, arrays are immutable value types [may be using these terms incorrectly]. The only way to modify your "array" in-place is to change the container to a mutable reference-type, like a List<byte>.

If you are really keen on "passing by ref", in-place, side-effect semantics, then one option may be the following

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

As for usage,

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

Under the hood, List<T> is maintaining an array of type T. At certain thresholds, it is simply manipulating the underlying array and\or instantiating new arrays for us.

Hope this helps! :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文