1
\$\begingroup\$

I have a project where I publish and subscribe some data packages. Mostly those data packages are just one package, but sometimes (1 in 100) there could be more packages at one time (a lot more, like 100.000 at the same time) I have a class PacketTransport which only has a IDataPacket property in it. I do this so I can serialize and deserialize it with JSON.net and the JsonSerializerSettings with TypeNameHandling = TypeNameHandling.Auto so the deserializer will know which kind of type it is.

I think about making IDataPacket to an IEnumerable<IDataPacket> so I will always enumerate eventhough there is just one data packet inside because when sometimes 100.000 Data Packages at a time are coming, my MQTT is overloaded with 100.000 PacketTransport packages and for the next minutes nothing else can pass. But as those 100.000 packets are mostly very small, I would like to pack it in one PacketTransport message.

So my question is: Is it bad to use an IEnumerable where most of the time only one item is inside it? Is the overhead that much or can I ignore it?

public class PacketTransport
{
    //Serialize with:
    //JsonConvert.SerializeObject(packetTransport, new() { TypeNameHandling = TypeNameHandling.Auto });
    public IEnumerable<IDataPacket> DataPackets { get; }

    public PacketTransport(IDataPacket dataPacket)
    {
        if (dataPacket is null) throw new ArgumentNullException(nameof(dataPacket));
        DataPackets = new[] { dataPacket }; //Is this the most efficient way?
    }

    public PacketTransport(IEnumerable<IDataPacket> dataPackets)
    {
        if (dataPackets is null) throw new ArgumentNullException(nameof(dataPackets));
        DataPackets = dataPackets.ToArray(); //Copy the original dataPackets
    }
}

public interface IDataPacket
{
    int Id { get; }
}

p.s.: I know, that my main bottleneck is probably the JSON serialization, I will go with protobuf in the future

\$\endgroup\$
1
  • 3
    \$\begingroup\$ It seems like you are worrying about micro optimization. Have you done any performance analyzis to spot the sweets spots / bottleneck of your system? Like via CodeTrack \$\endgroup\$ Commented Aug 4, 2021 at 9:29

2 Answers 2

2
\$\begingroup\$

Is it bad to use an IEnumerable where most of the time only one item is inside it?

This is perfectly fine. Checking for element counts and using either a collection or another variable would cause you more headaches and overhead than sticking to enumerables.


Is the overhead that much or can I ignore it?

You can ignore it... unless the profiler tells you otherwise.


DataPackets = new[] { dataPacket }; //Is this the most efficient way?

When you are looking for a materialized collection then I'm pretty sure it is and also the most elegant one. You could use linq like that but mhmm... I wouldn't:

Enumerable.Repeat(dataPacket , 1)
\$\endgroup\$
1
\$\begingroup\$

The only area I would consider for maintainability of your code is to avoid repeating code i.e. "DRY".

public class PacketTransport
{
    public IEnumerable<IDataPacket> DataPackets { get; }

    // constructor chaining
    public PacketTransport(IDataPacket dataPacket) 
        : this(new[] { dataPacket ?? throw new ArgumentNullException(nameof(dataPacket)) })
        { }

    public PacketTransport(IEnumerable<IDataPacket> dataPackets)
    {
        if (dataPackets is null) throw new ArgumentNullException(nameof(dataPackets));
        
        // now we have all the logic for construction in one place.
        DataPackets = dataPackets;
    }
}

Also note, that I don't see the value of enumerating your collection IDataPacket> dataPackets in your constructor, as it doesn't show any apparent value or "need".

DataPackets = dataPackets.ToArray(); // <-- no obvious reason why you are doing this...
\$\endgroup\$
2
  • \$\begingroup\$ In addition to DRY, you can add an extension method on object and do, something like dataPackets.ValueOrThrow() \$\endgroup\$
    – MaLiN2223
    Commented Aug 6, 2021 at 20:50
  • 3
    \$\begingroup\$ There is a good reason for DataPackets = dataPackets.ToArray(): copy the items from the passed IEnumerable<IDataPacket> and ignore future changes in it. Imagine you pass a List<IDataPacket> to the constructor and you are saving only a reference to this list by using DataPackets = dataPackets. If you remove or add items from this list later, your class will reflect this through DataPackets. Using some Linq for dataPackets values would be also an example. So it depends on the requirements. \$\endgroup\$
    – Steeeve
    Commented Aug 9, 2021 at 23:29

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.