将 ReadOnlyCollection<byte> 写入流
Writing ReadOnlyCollection<byte> to stream
我使用包含多个魔术字节序列的二进制格式。我想将它们作为不可变静态成员保存在静态 class 中。
public static class HuffmanConsts
{
// output format: Header, serialized tree (prefix), DataDelimiter, coded data (logical blocks are 8 byte large, Little Endian)
public const string Extension = ".huff";
public static readonly IReadOnlyList<byte> Header = Array.AsReadOnly(new byte[] {0x7B, 0x68, 0x75, 0x7C, 0x6D, 0x7D, 0x66, 0x66}); // string {hu|m}ff
public static readonly IReadOnlyList<byte> DataDelimiter = Array.AsReadOnly(BitConverter.GetBytes(0L)); // eight binary zeroes, regardless of endianness
}
ReadOnlyCollection<byte>
(从 Array.AsReadOnly()
返回)防止外部代码更改值,这与 byte[]
.
不同
但是现在,我无法通过stream.Write()
输出Header
,因为它需要byte[]
:
stream.Write(HuffmanConsts.Header, 0, HuffmanConsts.Header.Count)
Header
有没有优雅的写法?或者我必须写一个循环并将字节一个一个地送入流中吗?
只是让输出数组不可变
你可以这样考虑:
public static class HuffmanConsts {
// output format: Header, serialized tree (prefix), DataDelimiter,
// coded data (logical blocks are 8 byte large, Little Endian)
public const string Extension = ".huff";
private static readonly IReadOnlyList<byte> _header =
// string {hu|m}ff
Array.AsReadOnly(new byte[] {0x7B, 0x68, 0x75, 0x7C, 0x6D, 0x7D, 0x66, 0x66});
private static readonly IReadOnlyList<byte> _dataDelimiter =
// eight binary zeroes, regardless of endianness
Array.AsReadOnly(BitConverter.GetBytes(0L));
public static byte[] Header { get { return _header.ToArray(); } }
public static byte[] DataDelimiter { get { return _dataDelimiter.ToArray(); } }
}
处理 ToArray 的任何性能影响
但是,每次访问这些属性时都会产生 ToArray()
的开销。为了减轻潜在的性能损失(注意:测试是为了查看是否真的存在!),您可以使用 System.Buffer.BlockCopy
:
private static readonly byte[] _header =
// string {hu|m}ff
new byte[] {0x7B, 0x68, 0x75, 0x7C, 0x6D, 0x7D, 0x66, 0x66};
private static int BYTE_SIZE = 1;
private static byte[] GetHeaderClone() {
byte[] clone = new byte[_header.Length];
Buffer.BlockCopy(_header, 0, clone, 0, _header.Length * BYTE_SIZE);
return clone;
}
更好的解决方案:封装写入流
您还可以创建扩展方法,让您的消费者不再纠结自己编写这些流组件的细节,例如,WriteHeader
方法可能如下所示:
public static class StreamExtensions {
// include BlockCopy code from above
public static void WriteHuffmanHeader(this Stream stream) {
var header = GetHeaderClone();
stream.Write(header, 0, header.Length);
}
}
这不会使数组不可变,但私有化不是问题。
一个可能更好的解决方案:封装霍夫曼流 Object
您还可以选择实施自己的 HuffmanStream
,它会为您处理 header 和其他方面的细节!我实际上认为这是理想的,因为它将 Huffman 流的所有关注点封装到一段可测试的代码中,不会在您需要使用的每个地方都重复。
public class HuffmanStream : Stream {
private Stream _stream = new MemoryStream();
private static byte[] _header = ... ;
public HuffmanStream( ... ) {
...
_stream.Write(_header, 0, _header.Length)
// the stream already has the header written at instantiation time
}
}
注意:当将 byte[]
实例传递给 Stream.Write()
时,它可能会在方法 returns 之后被修改,因为该方法可以直接访问数组。 Well-behaved Stream
实现不这样做,但为了防止自定义流安全,您必须 将 Stream
实例视为敌对 ,因此永远不会通过它们是对不应更改的数组的引用。例如,任何时候您想将 _header
字节数组传递给 possiblyHostileStream.Write()
,您都需要传递 _header.Clone()
。我的 HuffmanStream
不需要这个,因为它使用 MemoryStream
,可以信任。
您可以保持 class 不变,将 Header
转换为 byte[]
用于流:
stream.Write(HuffmanConsts.Header.ToArray(), 0, HuffmanConsts.Header.Count)
此 IEnumerable.ToArray()
扩展方法来自 System.Linq
.
或者,您可以直接存储字节数组并使用 属性 到 return 它的克隆。这是 的一个更简单的变体。不再需要 ReadOnlyCollection
。
public static class HuffmanConsts
{
// output format: Header, serialized tree (prefix), DataDelimiter, coded data (logical blocks are 8 byte large, Little Endian)
public const string Extension = ".huff";
private static byte[] _header = new byte[] {0x7B, 0x68, 0x75, 0x7C, 0x6D, 0x7D, 0x66, 0x66}; // string {hu|m}ff
private static byte[] _dataDelimiter = BitConverter.GetBytes(0L); // eight binary zeroes, regardless of endianity
public byte[] Header { get { return (byte[])_header.Clone(); } }
public byte[] DataDelimiter { get { return (byte[])_dataDelimiter.Clone(); } }
}
我不赞成这个解决方案,因为这些属性做 non-trivial 工作量(分配;不过仍然是 O(1))。根据 Framework Design Guidelines.
,将它们转换为 Get*
方法可以传达想法,并且是发布不可变数组的方式。
正如 Ivan Stoev 在问题下评论的那样:
Stream
requires byte[]
. Dot. You need to sacrifice either some OOP concepts or performance. The choice is yours.
原因是(我猜)字节数组直接传递给底层系统调用,而其他集合具有不兼容的内部结构。因此,我相信,如果您想保留 HuffmanConsts
.
的当前实现,则不可能避免每次调用时新数组分配引入的开销。
我使用包含多个魔术字节序列的二进制格式。我想将它们作为不可变静态成员保存在静态 class 中。
public static class HuffmanConsts
{
// output format: Header, serialized tree (prefix), DataDelimiter, coded data (logical blocks are 8 byte large, Little Endian)
public const string Extension = ".huff";
public static readonly IReadOnlyList<byte> Header = Array.AsReadOnly(new byte[] {0x7B, 0x68, 0x75, 0x7C, 0x6D, 0x7D, 0x66, 0x66}); // string {hu|m}ff
public static readonly IReadOnlyList<byte> DataDelimiter = Array.AsReadOnly(BitConverter.GetBytes(0L)); // eight binary zeroes, regardless of endianness
}
ReadOnlyCollection<byte>
(从 Array.AsReadOnly()
返回)防止外部代码更改值,这与 byte[]
.
但是现在,我无法通过stream.Write()
输出Header
,因为它需要byte[]
:
stream.Write(HuffmanConsts.Header, 0, HuffmanConsts.Header.Count)
Header
有没有优雅的写法?或者我必须写一个循环并将字节一个一个地送入流中吗?
只是让输出数组不可变
你可以这样考虑:
public static class HuffmanConsts {
// output format: Header, serialized tree (prefix), DataDelimiter,
// coded data (logical blocks are 8 byte large, Little Endian)
public const string Extension = ".huff";
private static readonly IReadOnlyList<byte> _header =
// string {hu|m}ff
Array.AsReadOnly(new byte[] {0x7B, 0x68, 0x75, 0x7C, 0x6D, 0x7D, 0x66, 0x66});
private static readonly IReadOnlyList<byte> _dataDelimiter =
// eight binary zeroes, regardless of endianness
Array.AsReadOnly(BitConverter.GetBytes(0L));
public static byte[] Header { get { return _header.ToArray(); } }
public static byte[] DataDelimiter { get { return _dataDelimiter.ToArray(); } }
}
处理 ToArray 的任何性能影响
但是,每次访问这些属性时都会产生 ToArray()
的开销。为了减轻潜在的性能损失(注意:测试是为了查看是否真的存在!),您可以使用 System.Buffer.BlockCopy
:
private static readonly byte[] _header =
// string {hu|m}ff
new byte[] {0x7B, 0x68, 0x75, 0x7C, 0x6D, 0x7D, 0x66, 0x66};
private static int BYTE_SIZE = 1;
private static byte[] GetHeaderClone() {
byte[] clone = new byte[_header.Length];
Buffer.BlockCopy(_header, 0, clone, 0, _header.Length * BYTE_SIZE);
return clone;
}
更好的解决方案:封装写入流
您还可以创建扩展方法,让您的消费者不再纠结自己编写这些流组件的细节,例如,WriteHeader
方法可能如下所示:
public static class StreamExtensions {
// include BlockCopy code from above
public static void WriteHuffmanHeader(this Stream stream) {
var header = GetHeaderClone();
stream.Write(header, 0, header.Length);
}
}
这不会使数组不可变,但私有化不是问题。
一个可能更好的解决方案:封装霍夫曼流 Object
您还可以选择实施自己的 HuffmanStream
,它会为您处理 header 和其他方面的细节!我实际上认为这是理想的,因为它将 Huffman 流的所有关注点封装到一段可测试的代码中,不会在您需要使用的每个地方都重复。
public class HuffmanStream : Stream {
private Stream _stream = new MemoryStream();
private static byte[] _header = ... ;
public HuffmanStream( ... ) {
...
_stream.Write(_header, 0, _header.Length)
// the stream already has the header written at instantiation time
}
}
注意:当将 byte[]
实例传递给 Stream.Write()
时,它可能会在方法 returns 之后被修改,因为该方法可以直接访问数组。 Well-behaved Stream
实现不这样做,但为了防止自定义流安全,您必须 将 Stream
实例视为敌对 ,因此永远不会通过它们是对不应更改的数组的引用。例如,任何时候您想将 _header
字节数组传递给 possiblyHostileStream.Write()
,您都需要传递 _header.Clone()
。我的 HuffmanStream
不需要这个,因为它使用 MemoryStream
,可以信任。
您可以保持 class 不变,将 Header
转换为 byte[]
用于流:
stream.Write(HuffmanConsts.Header.ToArray(), 0, HuffmanConsts.Header.Count)
此 IEnumerable.ToArray()
扩展方法来自 System.Linq
.
或者,您可以直接存储字节数组并使用 属性 到 return 它的克隆。这是 ReadOnlyCollection
。
public static class HuffmanConsts
{
// output format: Header, serialized tree (prefix), DataDelimiter, coded data (logical blocks are 8 byte large, Little Endian)
public const string Extension = ".huff";
private static byte[] _header = new byte[] {0x7B, 0x68, 0x75, 0x7C, 0x6D, 0x7D, 0x66, 0x66}; // string {hu|m}ff
private static byte[] _dataDelimiter = BitConverter.GetBytes(0L); // eight binary zeroes, regardless of endianity
public byte[] Header { get { return (byte[])_header.Clone(); } }
public byte[] DataDelimiter { get { return (byte[])_dataDelimiter.Clone(); } }
}
我不赞成这个解决方案,因为这些属性做 non-trivial 工作量(分配;不过仍然是 O(1))。根据 Framework Design Guidelines.
,将它们转换为Get*
方法可以传达想法,并且是发布不可变数组的方式。
正如 Ivan Stoev 在问题下评论的那样:
Stream
requiresbyte[]
. Dot. You need to sacrifice either some OOP concepts or performance. The choice is yours.
原因是(我猜)字节数组直接传递给底层系统调用,而其他集合具有不兼容的内部结构。因此,我相信,如果您想保留 HuffmanConsts
.