[英]Having trouble with extension methods for byte arrays
我正在使用发送回图像的设备,当我请求图像时,图像数据之前会包含一些未记录的信息。 我只能通过查看二进制数据并识别其中的图像头信息来实现这一点。
我最初有一个普通方法,然后将其转换为扩展方法。 最初的问题与编译器抱怨没有将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();
}
我现在的问题是,看起来扩展方法的第一个此参数是按值传递的。 我想重新分配数组指向的内容,但是在这种情况下,看来我只需要操纵数组的数据即可。
扩展方法是静态方法,仅看起来是实例方法。 您可以认为扩展方法正在处理的实例是只读的(按值)。 分配给扩展名的第一个参数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;
}
}
很抱歉,我不知道您遇到了什么具体问题,或如何解决该问题[确定检查引用了命名空间并解决使用相似命名方法的任何冲突是一个开始],但是我确实注意到一两个怪异之处。
考虑以下示例解决方案,
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<byte>
。
如果您确实热衷于就地产生副作用语义“通过引用传递”,则可以选择以下方法:
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>
维护着一个类型T
的数组。 在某些阈值下,它只是为我们操纵底层数组和/或实例化新数组。
希望这可以帮助! :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.