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ますか? または、ループを記述して、バイトを 1 つずつストリームにフィードする必要がありますか?

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

これにより配列が不変になることはありませんが、プライベートであることは問題ではありません。

おそらくさらに良い解決策: ハフマン ストリーム オブジェクトをカプセル化する

HuffmanStreamヘッダーの詳細やその他の側面を処理する独自のものを実装するオプションもあります! ハフマンストリームのすべての問題を、作業する必要があるすべての場所で複製されないテスト可能なコードにカプセル化するので、これは実際には理想的だと思います。

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()信頼できる をHuffmanStream使用しているため、これは必要ありません。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) ですが)。Framework Design GuidelinesGet*によると、それらをメソッドに変換すると、アイデアが伝わり、不変の配列を公開する際の方法となります。


Ivan Stoevが質問の下でコメントしたように:

Streamが必要byte[]です。ドット。OOP の概念またはパフォーマンスのいずれかを犠牲にする必要があります。選択はあなた次第です。

その理由は、バイト配列が基になるシステムコールに直接渡され、他のコレクションの内部構造に互換性がないためです。したがって、 の現在の実装を維持したい場合、呼び出しごとに新しい配列割り当てによって導入されるオーバーヘッドを回避することはできないと思いますHuffmanConsts

于 2016-01-11T19:21:05.090 に答える