0
\$\begingroup\$

I am working on library project in which I am reading and writing binary files that have own file format structure. In that structure there are variable-length and fixed-length elements. My approach is to parse byte stream of such files, create corresponding objects, and then get current byte stream and write it back to file. So I need to have byte stream <-> object binding. The problem is very complicated file format (AFP) in which some structures are variable length and some are not. I got stuck on choosing best "list" class for storing byte streams that may be fixed-length or variable-length. My idea is to wrap such byte stream "list" in another class (composition) and allow different external objects to be passed into that class (C# array or List).

namespace TEST
{
    public class ByteStreamList : IList<byte>, IEnumerable<byte>
    {
        protected IList<byte> bytes = null;

        public byte this[int index] {
            get {
                return bytes[index];
            }

            set {
                bytes[index] = value;
            }
        }

        public int Count {
            get {
                return bytes.Count;
            }
        }

        public bool IsReadOnly {
            get {
                return bytes.IsReadOnly;
            }
        }

        public ByteStreamList(IList<byte> bytes)
        {
            this.bytes = bytes;
        }

        public void Add(byte item)
        {
            if (!(bytes is IReadOnlyList<byte>)) {
                bytes.Add(item);
            }
        }

        public void Clear()
        {
            if (!(bytes is IReadOnlyList<byte>)) {
                bytes.Clear();
            }
        }

        public bool Contains(byte item)
        {
            if (!(bytes is IReadOnlyList<byte>)) {
                return bytes.Contains(item);
            }

            return false;
        }

    // ... rest of IList<byte> and IEnumerable<byte> interface ///
}

namespace Test_CA
{
    public class Program
    {
        public static void Main(string[] args)
        {
            ByteStreamList fixedLengthByteList = new ByteStreamList(new byte[] { 0xAA, 0xBB, 0xCC });
            ByteStreamList variableLengthByteList = new ByteStreamList(new List<byte>() { 0xDD, 0xEE, 0xFF });
        }
    }
}

With that approach (using IList<> class) I would need to provide some access to byte stream via IList<> methods. So I could let my wrapper class implement IList<>. That approach forces me to check if underlying "list" class is IReadOnlyList or not. If not, methods like Add() Clear() would be allowed. I know it is very bad approach to use List<> because if something will require that type I will not pass any other list/container except List<> .

Is it good approach with checking everytime if "list" is readonly?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I would think you would rather use the IList.IsFixedSize property concerning Add, Clear, Remove, or RemoveAt actions.

Your indexer logic seems lacking. Granted you could make a custom exception for an out-of-range index, but it will be thrown anyway. More to my point and to align with your stated objective, the indexer setter is where you would want to check IsReadOnly.

I would also encourage you to add more constructors, specifically at least one for IEnumerable<byte> as an input parameter, but you may want the constructor to include bool parameters for IsReadOnly and IsFixedSize. As you have it know accepting only another IList as input, you retain the properties of that input. If you were to accomodate IEnumerable<byte> as input, you may want to allow the caller to the constructor to declare the intentions for IsReadOnly and IsFixedSize.

Consider this possible signature:

public ByteStreamList(IEnumerable<byte> bytes, bool isReadOnly, bool isFixedSize)

This poses a challenge of what if someone calls this with an IList with different properties than what is passed in the constructor? What if they pass in a fixed-size, read-only IList with:

var sbl = new ByteStreamList(fixedSizeReadOnlyIList, false, false);

The constructor would have to compose the underlying bytes accordingly and not just simply assign fixedSizeReadOnlyIList to bytes. Could be a fun challenge.

\$\endgroup\$
1
  • \$\begingroup\$ I tried to add exceptions for Add(), Clear(), Remove() and RemoveAt() methods, but I am not sure if such code would look pretty & clear. I like idea of passing bools into constructor and selecting corresponding behavior. Would be it clear & OK to create static methods that wraps (IEnumberable<byte>, bool, bool) constructor and passes values for bool fields? BSL.CreateFixedSizeList(IEnumberable<byte>) or BSL.CreateReadOnlyList(IEnumberable<byte>), BSL.CreateFixedSizeReadOnlyList(IEnumberable<byte>) ? Maybe examine if input list is really RO or FS in those static methods? \$\endgroup\$
    – shjeff
    Commented Jul 4, 2022 at 8:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.