2

我使用包含几个魔术字节序列的二进制格式。我想将它们作为不可变的静态成员保存在静态类中。

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[].

但是现在,我无法Header通过输出stream.Write(),因为它需要byte[]

stream.Write(HuffmanConsts.Header, 0, HuffmanConsts.Header.Count)

有没有优雅的写法Header?还是我必须编写一个循环并将字节一个一个地输入流中?

4

2 回答 2

6

只是使输出数组不可变

你可以考虑这样的事情:

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

这不会使数组不可变,但私有不是问题。

一个可能更好的解决方案:封装 Huffman Stream 对象

您还可以选择实现自己的HuffmanStream,它会为您处理标题的细节和其他方面!我实际上认为这是理想的,因为它将 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(),可能会在方法返回后对其进行修改,因为方法可以直接访问数组。行为良好的Stream实现不会这样做,但为了防止自定义流,您必须Stream实例视为敌对的,因此永远不要将对不应更改的数组的引用传递给它们。例如,任何时候您想将_header字节数组传递给possiblyHostileStream.Write(),都需要传递_header.Clone()。MyHuffmanStream不需要这个,因为它使用MemoryStream,这是可以信任的。

于 2016-01-11T17:52:54.587 回答
1

您可以将课程保持原样并将其转换Headerbyte[]

stream.Write(HuffmanConsts.Header.ToArray(), 0, HuffmanConsts.Header.Count)

IEnumerable.ToArray()扩展方法来自System.Linq.

或者,您可以直接存储字节数组并使用属性返回其克隆。这是ErikE 描述的第一种方法的一个更简单的变体。不再需要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(); } }
}

我不赞成这个解决方案,因为这些属性做了大量的工作(分配;尽管仍然是 O(1))。根据框架设计指南,将它们转换为Get*方法可以传达这个想法,并且是发布不可变数组时要走的路。


正如 Ivan Stoev 在问题下评论的那样:

Stream需要byte[]. 点。您需要牺牲一些 OOP 概念或性能。这是你的选择。

原因是(我猜)字节数组直接传递给底层系统调用,而其他集合具有不兼容的内部结构。因此,我相信,如果您想保留HuffmanConsts.

于 2016-01-11T19:21:05.090 回答