Returning byte[] vs. Stream in C#

May 17th, 2024
Posted in C# | No Comments

Implementing resource management within software has been a problem faced by developers for decades. Conventions such Resource Allocation is Initialization (RAII) and language features such as Rust’s ownership concept are attempts at addressing the resource management problem.

Deciding between returning a byte[] or Stream object from a C# method requires thoughtful consideration. There is a tradeoff between efficient memory use and resource management safety. Are Stream objects better than byte[] objects when passing around file content within a software system? As happens frequently in engineering efforts, the better solution depends on the problem’s conditions.

In situations where average file sizes are a considerable percentage of total system memory, such as 500MB files on a system with 4GB of RAM, there is a strong case for using Stream objects and accepting the responsibility of resource management as opposed to allocating large chunks of memory. By using Stream objects, the developer is responsible for ensuring timely disposal of resources and release of file handles. The developer is responsible for preventing situations where freed resources are used and where resources are freed multiple times or never at all.

Similarly, where average file sizes are less than 1024 bytes, there is a strong case for using byte[] and trusting the runtime with resource management and garbage collection. byte[] objects do not implement IDisposable, and the environment is responsible for managing their resources.

The choice between byte[] and Stream objects is more interesting when the average size of files being manipulated is between the above extremes. Theoretically, 4000 1MB files can be loaded into memory on a system with 4GB of RAM. The decision between using byte[] or Stream objects depends on the software’s typical usage. If the software typically manipulates hundreds of thousands of such files concurrently, Stream objects will likely be the better option.

But, should Stream objects be used if the system manipulates a couple hundred 1MB files concurrently in the worst case?

The existence of the following exceptions exemplifies the persistent difficulty of resource management even in an environment that provides garbage collection:

  • System.ObjectDisposedException: Cannot access a disposed object.
  • System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling ‘Dispose’ on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.

I prefer a safe approach that depends on the software environment and runtime over a risky idealized general solution that requires developer effort to manage resources. I prefer a fitted solution rather than one that is over-engineered. On a system with 4GB of memory, I prefer an implementation where 200MB of memory is allocated in the worst case to store file content with automatic resource management rather than an implementation where 2MB of memory is used but requires developer diligence. In this situation, I prefer byte[] objects over Stream objects.

Reading List: 2024Q1

March 31st, 2024

In an effort to develop and maintain skills, I read the following books in the last three months:

Heatmap: 2023FY-2024Q1

March 18th, 2024

Raspberry Pi Zero W RNDIS to Windows 11

February 23rd, 2024

Pi Configuration Procedures (on Raspberry Pi Zero W):

  1. Modify /boot/config.txt, append the following as a separate line:
    dtoverlay=dwc2
  2. Modify /boot/config.txt, comment out any lines containing otg_mode.
  3. Modify /boot/cmdline.txt, add the following immediately after rootwait, surrounded by spaces:
    modules-load=dwc2,g_ether
  4. Create an empty file called /boot/ssh.

Driver Installation Procedures (on Microsoft Windows 11):

  1. Disable driver signature verification.
  2. Download linux.inf file.
  3. In Device Manager, under Ports, right-click on USB Serial Device (the one that appears when connecting the Pi to the computer’s USB port.
  4. Click on Update Driver > Browse my computer for drivers > Let me pick from a list of available drivers on my computer.
  5. Click on Have Disk… and browse to the folder with the downloaded linux.inf file. Click OK.
  6. With “Show compatible hardware” ticked, “Linux USB Ethernet/RNDIS Gadget” should be an option. Select this entry and click Next.
  7. Proceed with remainder of driver installation.

Connection Procedures (on Microsoft Windows 11)

  1. Using SSH program, connect to Pi’s hostname with “.local” appended to it. For example:
    ssh myusername@myraspberrypi.local

一番好きな歌

February 17th, 2024
Posted in 日本語 | No Comments

アニソンが好きですが、この歌が一番大好きです。

Mongol800の「小さな恋のうた」

このライブは楽しそうです。いい音だと思っています。いつかMongol800の演奏を見に行きたいです。陳腐ちんぷですか。