C# 编写只读集合<;字节>;流

C# 编写只读集合<;字节>;流,c#,filestream,immutability,binaryfiles,readonly-collection,C#,Filestream,Immutability,Binaryfiles,Readonly Collection,我使用一种二进制格式,它包含几个神奇的字节序列。我想将它们作为不可变的静态成员保存在静态类中 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 =

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

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
}

是否有一种优雅的方式来编写
标题
?还是我必须写一个循环并将字节逐个输入到流中?

只是使输出数组不可变

你可以考虑这样的事情:

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(); } }
}
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);
   }
}
更好的解决方案:将写入封装到流中

您还可以创建扩展方法,让您的使用者不再忙于编写这些流组件本身的细节,例如,
WriteHeader
方法可能如下所示:

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

您还可以选择实现自己的
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
   }
}


注意:当将
字节[]
实例传递给
Stream.Write()
时,当方法直接访问数组时,可能会在方法返回后对其进行修改。行为良好的
Stream
实现不会这样做,但为了对自定义流安全,您必须将
Stream
实例视为恶意实例,因此绝不向它们传递对不应更改的数组的引用。例如,任何时候,如果您想将
\u头
字节数组传递给
可能的hostilestream.Write()
,则需要传递
\u头.Clone()
。My
HuffmanStream
不需要它,因为它使用了可以信任的
MemoryStream

您可以保持类的原样,并将流的
转换为
字节[]

stream.Write(HuffmanConsts.Header.ToArray(), 0, HuffmanConsts.Header.Count)
此扩展方法来自
System.Linq

或者,您可以直接存储字节数组,并使用属性返回其克隆。这是的一个更简单的变体。不再需要
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在问题下所说:


需要
字节[]
。圆点。您需要牺牲一些OOP概念或性能。选择权在你


原因是(我猜)字节数组直接传递给底层系统调用,而其他集合的内部结构不兼容。因此,我认为,如果要继续搜索当前的
HuffmanConsts

实现,就不可能避免每次调用时新数组分配带来的开销,MSDN并尝试了类似于
stream.write readonly collection byte
的查询,但未获得相关结果。
stream
需要
byte[]
。圆点。您需要牺牲一些OOP概念或性能。选择权在你。我将封装序列化/反序列化本身。考虑一个类,使用[静态]方法无效写入头(流)、Read Edditer(流)、Read Head(流),编辑看起来很棒。我用不同的方法编写了自己的答案,但是围绕实际流编写HuffmanStream包装器可能是最终的解决方案。需要写入不能均匀划分为字节的位序列,因此HuffmanStream可能也会为这些位序列的表示实现Write()。实际上,
ReadOnlyCollection
,@usr始终需要善意的假设。通过一个cast,您可以从
IReadOnlyList
界面获得它并使用它,然后您就进入了只读外观的后面。我认为将底层数组传递给
流的
Write
方法是可以的,因为流没有理由改变数组。正如我读到的,这实际上是违反了合同。如果我错了,请纠正我。@Palec总是使用
someList.AsReadOnly()
来填充
IReadOnlyCollection
IReadOnlyList
。你不能改变这一点,不需要诚信。而且,
Stream
是抽象的,因此
类恶意流:Stream
不安全。
Stream
的任何MS编写的子类,如
FileStream
MemoryStream
都是安全的。啊啊!这就是我错过的。@Palec它可能会违反合同,没错。这里的相关场景是多个不合作方生活在同一个.NET进程中。这就是为什么BCL如此严肃地不允许这样做。如果一个库出现故障,会导致其他组件出现可怕的错误。如果您控制了所有这些代码,我就不必担心这些,只需公开这些数组。或者使用这个问题中99%的保护方法。每天学习新的东西。我的C#处于中级,只是雷达上没有克隆,所以谢谢你!它比Buffer.BlockCopy
要好(它仍然可以将目标的一部分复制到目标的一部分,而克隆显然不行)。你介意我更新我的答案使用
Clone
(并在评论中留下关于
BlockCopy
的原始建议,这样你的答案仍然有意义吗)?是的,我可以。