General use library inspired by common patterns seen in production code.
The goal of this project is to help make code easier to read and more efficient. Secondary to that is to learn and practice CI, high coverage unit testing, and otherwise polishing code to a degree that one many times cannot at work.
I love myself a good philosophical debate about code. Feel free to provide constructive criticism.
It may not be exciting code, but that's why I am excited about it.
Over these many decades roaming life, I have come to believe that the quality of a product is not defined by how good it typically is, but how bad it can be. What can go wrong will go wrong, at the most inopportune time, and in the worst way possible. Boring code shouldn't supprise you.
Light weight collections that implement common interfaces.
It is common to have to pass an empty collection. While there exists some common ones in the BCL like Array.Empty
or Enumerable.Empty
, they are spread around, sub-optimal, and there is not an Empty
for all of the common interfaces.
Empty collection are special in that many aspects of them can be stateless. For example, there is no reason to allocate a new enumerator when iterating them.
In my expereience, it is very common to have a collection of a single element. While I wanted to name it Single
, that conflicts with a synonym with Float
.
I can't tell you how many times I've seen new Dictionary {{ key, value }}
. Not only does the Dictionary
collection need to make an Array
, it also needs to hash the key. When working with a single collection, you can just hold the single KeyValue
and skip hashing on both creation and lookups. Similar optimizations can be made to other collections when you only have a single element.
Also in my expereience, many of these single element collections tend to be allocated in hot paths. Sizable reductions in GC and CPU time can be had while making the code easier to read. Win-win-win.
Admit it. You've had it where some quickly slapped together project throws a KeyNotFound
exception and you get driven crazy that you have no idea what key
caused the exception.
I was originally going to create a new class that wrapped a Dictionary
, but that would cause more code paths to have to be tested and meant two objects would be created. I played around with just inheriting from Dictionary
and it worked well. It allowed for a minimal class that only changed the few methods that I cared to change with the additional benefit that this can be downcasted into a Dictionary
for those times someone didn't give an IDictionary
signature.
For now, it's just SimpleMetadata
Some times you have coode that looks like
void SomeMethod(Guid id, string typeId)
And it's just Guid
this and string
that. So you want to make proper types so you code can look like
void SomeMethod(EntityId id, EntityTypeId typeId)
But can be a lot of yak shaving if you want to have something like Dictionary<EntityId, value>
. You can instead do something like
public class EntityId : SimpleMetadata<Guid>
{
public EntityId(string value)
: base(value) { }
}
Now you have EntityId
without all of the setup.
And if you have some special situation, you can easily override the behavior, like case insensitive.
public abstract class EntityTypeId : SimpleMetadata<string>
{
protected override IEqualityComparer<string> EqualityComparer { get { return StringComparer.OrdinalIgnoreCase; } }
protected override IComparer<string> Comparer { get { return StringComparer.OrdinalIgnoreCase; } }
protected EntityTypeId(string value)
: base(value) { }
}
And since case insensetive is such a common thing, I already included a class CaseInsensitiveMetadata
that is implemented like the above EntityTypeId
example.
And yes, ==
is overriden. So you get this out of the box.
public class EntityId : SimpleMetadata<Guid>
{
public EntityId(string value)
: base(value) { }
}
Guid value = Guid.NewGuid();
EntityId id = new EntityId(value);
EntityId id2 = new EntityId(value);
Assert.True(id == id2);
Assert.True(id == value);