Home
Blog
Recent Posts
Updated
Dot Net Perls

Is Object-Oriented Programming Worth It

Some decades ago a new programming concept became popular: object-oriented programming. The basic unit of program design would become the class, and classes could contain methods and fields, and inherit from other classes. Runtime types could determine what methods were called.

All was great, and programs became perfect, with no further bugs ever being introduced. Back in reality, though, OOP introduced a lot of complexity, and it was hard for even experienced developers to grasp. And after learning about OOP and writing about it, I think of it mainly as a program organization strategy.

Here are some of my realizations about OOP:

Runtime type detection is no different from if-statements and switches on a field—it seems "more advanced" to call a derived or virtual method, but in reality, it is just another conditional branch.
Some OOP concepts seem more advanced than traditional code like if-statements, but they are just more complex to use, and the end result is the same.
OOP concepts are a way to organize programs in a structured way (around classes), and this is the key advantage.

In conclusion, object-oriented programming is nothing special when it comes to the runtime execution of programs. But it makes it possible to organize programs in a more complex (and possibly harder-to-understand) way. These organizational constructs can be beneficial on larger programs, but are rarely worthwhile on smaller ones.

The Problem With Swift

Swift is a general-purpose programming language with clear syntax. When I first began learning Swift around the year 2010, I was excited about the language and felt it could be one of the leading programming languages in the future.

However, as years passed, I began to feel disillusioned about Swift. The major problem I encountered was that each time the language was updated, many of my example programs stopped working. The language kept breaking itself on each version. This was not just a pre-1.0 language issue—Swift 1.0 had been released already.

Even in the Swift 5.0 era, things as simple as looping over the characters in a string kept changing. Programs would not compile in newer compilers. Function arguments now had to be specified in a different way, and tons of errors and warnings would appear on previously-correct code.

A computer language is not an art project—it is something that people rely on to build programs. Perhaps early Swift versions were not yet ready to be released, and Apple wanted to continue changing the language to fit their internal needs for macOS and iOS programs. In any case Swift (at least in 2010) ended up being a poor choice for outside developers.

When to Use a Struct in C#

When I was first learning how to program C#, I wanted to use structs in strategic places. It seemed to make sense—structs could somehow make my programs better and speed them up somehow.

Over time, I started to realize most of the places I used structs would have been better off (or just as well-off) with classes. Structs are a way to have a type that is stored inline, without a reference to the heap memory. In .NET things like ints, DateTime, or KeyValuePair are structs—these are all types that require minimal memory.

Unless you have a need to create a new numeric type, it is best to avoid structs. Here are some drawbacks of structs:

They are often larger than a reference to the heap, so when we pass them to a method, or copy them to a List, more memory needs to be copied—this causes slowdowns.
If the struct is larger than a reference, a List of structs may be larger in memory (and slower) than a List of class references.

The C# language is designed mostly around classes. Structs are more of a feature that is needed for the standard library—to create types like int, DateTime, or KeyValuePair. If you have types similar to those, a struct can help performance slightly, but even then a class may be superior. In general, classes are preferred.

When to Use a Lookup Table

Code often has "hot" spots that are reached repeatedly—and these places may have a branching condition. For example, in a spelling checker, each byte of the input might need to be checked to see if it is part of a word, whitespace, or punctuation.

Lookup tables can be used to reduce the amount of branching done in these places. Usually a lookup table has 256 bytes, one array element for each possible byte, and can store values like a boolean or another byte. In performance, lookup tables occupy space on the processor's level 1 cache. Sometimes, a delay can occur trying to copy the memory to the cache.

For this reason, lookup tables can actually slow down programs—the delay in copying memory is greater than the time saved in reducing branching. Lookup tables still have an advantage in some places:

If the lookup table is used many times, it probably saves more time than that spent copying the memory for it.
If the lookup table makes the surrounding code simpler, this is a worthy goal and an improvement.

Benchmarks tend to make lookup tables appear faster than they are, as the table remains in the processor's cache throughout the benchmark run. Still, if enough branches are saved, the lookup table will improve overall performance. My personal rule is that any time a table is accessed more than 20 times in a function, and the results of the table are hard to predict, it is worth using a lookup table.

When to Use StringBuilder in C#

It is common to need to append one string to another in C# programs. This is called concatenation. In languages where strings can be modified after being created, this is efficient—but in C#, strings are immutable and concatenation leads to a new string creation.

Creating too many new strings will cause excessive allocations and copying of memory. This can lead to big performance problems. StringBuilder can eliminate this problem by providing a buffer into which all the strings are copied, with fewer allocations.

We can use StringBuilder when:

We need to create a new string from many existing strings.
We want to append or concatenate two or more strings.

In the case of two strings, it can be faster just to concatenate the strings directly, but it is also acceptable to use StringBuilder. If further appends are needed, the StringBuilder will prevent future performance problems. So it is useful as a preventative measure against excess string copies.

Advantages of Enums in Rust

I do not know everything about every computer language, or even every one I write about on this site. Instead, I learn as I go along. From other languages like C# I was familiar with the concept of enums—types that store known values and can be referenced by name.

But in Rust, enums have an additional feature—they are an algebraic data type. This means a value in an enum can reference some data—like a struct instance, a String, a tuple or a number.

In practice, this means we can use a single enum to refer to multiple data types:

An enum with two variants, each with a different data type, can be tested in a match statement and we can evaluate the data at runtime.
We do not need to know which kind of attached data an enum instance will have until runtime.

So we can create a Color enum, and it could have a value of Red with a String, or a value of Blue with a usize instead. And we can refer the Color as meaning either Red or Blue—and a String or usize only in appropriate cases. This can simplify some programs where we must return many types of data from a single function.

The Problem With Windows Forms and WPF

There are many articles about Windows Forms and WPF on this site—and they have been helpful to people for a long time. With Windows Forms and WPF developers can make Windows applications that have windows, buttons, dialog boxes, and C# or VB.NET code.

But as competing technologies like web browsers have become more powerful and widespread, Windows Forms and WPF have become less useful. It is possible to build a simple web server in nearly any language that serves HTML pages and handles input from clicks and POST requests. And web browsers have many advantages over WinForms and WPF:

They are available on all platforms, even mobile phones.
Due to the popularity of the Internet, web browsers will be supported and maintained for a long time.
With time more native features like WASM and better UI controls will become available.

For some applications that need native speed, like text editors, web browsers and even games, it is necessary to use native UI controls. But for the vast majority of applications that one might need, web browsers are probably a better platform to target. Of course, for older applications that have already been written, continuing to support Windows Forms and WPF is necessary.

Rust Borrow Checker Tips

Suppose you are writing a Rust program, and you are having problems with the borrow checker. The compiler is giving you messages about "use of moved value" and "cannot borrow data as mutable." Should you give up?

No, you should not give up. Instead, here are some tips for dealing with the borrow checker.

Store all elements in a struct, and then reference those elements by index from elsewhere in the program.
Use indexes (like usize values) instead of references.
Clone structs when needed to fix some "moved value" issues, and wrap structs in Arcs to make cloning much faster.

Basically in Rust it helps to know what struct owns all the memory in a program. Then you don't have to worry about the memory anymore—you can just access the data by index, referencing the owner struct. In this approach, most structs do not need to store references to other structs.

When to Use a Hash Table

When we write code in a language like C#, Java, Python or Rust we often have to choose between arrays or lists and hash tables. An array or list stores elements one after another. A hash or dictionary stores elements in locations that are based on the values of the elements themselves (hash codes).

Often we can realize a performance improvement in programs by using hashed collections correctly. If our program loops over an array to find an element by value, it is likely faster to use a hash table instead.

Here are some signs it is worth trying a dictionary:

We often need to test if an element is contained in a collection of elements—has the element already been added?
We often need to insert or remove elements (not just at the end).
Nested loops can lead to significant slowdowns—could an inner loop be replaced with a hash table lookup?
We want to represent sparse data, such as an array where only a small number of elements have existing values.

If a program needs the packed elements in a linear collection, arrays or lists are a better choice. They are more memory-efficient and faster to loop over. But if the program has excessive searching by value, a hash collection is probably an improvement.

The Problem With 2D Arrays

2D arrays are a common topic that people want to know about. How can you make a 2D array in C#, Python, Java, Go—what is the syntax, how can you access elements? But for the most part the articles I have on 2D arrays ignores the main problems with 2D arrays.

Basically the best time to use a 2D array is never. It is usually better to just use one-dimensional arrays, nested arrays (which are like jagged arrays), or even hashtables. Often, data are sparse, and 2D arrays in most languages use memory for all elements—this wastes a lot of memory.

Here are some things I have found:

2D arrays end up having syntax that is complicated and hard to read.
Other data structures like nested arrays or hashtables are more memory-efficient (and faster).
For numerical processing, using third-party libraries is almost always a better solution than trying to do things directly with 2D arrays.

2D arrays, like recursive methods, are a feature that most programs would be better off not using. Other solutions are simpler and usually faster. I suppose, for completeness, learning about 2D arrays is worthwhile however.

More
Dot Net Perls is a collection of pages with code examples, which are updated to stay current. Programming is an art, and it can be learned from examples.
Donate to this site to help offset the costs of running the server. Sites like this will cease to exist if there is no financial support for them.
Sam Allen is passionate about computer languages, and he maintains 100% of the material available on this website. He hopes it makes the world a nicer place.
An RSS feed is available for this blog.
Home
Changes
© 2007-2025 Sam Allen