OutWit.Common
1.2.5
dotnet add package OutWit.Common --version 1.2.5
NuGet\Install-Package OutWit.Common -Version 1.2.5
<PackageReference Include="OutWit.Common" Version="1.2.5" />
<PackageVersion Include="OutWit.Common" Version="1.2.5" />
<PackageReference Include="OutWit.Common" />
paket add OutWit.Common --version 1.2.5
#r "nuget: OutWit.Common, 1.2.5"
#:package OutWit.Common@1.2.5
#addin nuget:?package=OutWit.Common&version=1.2.5
#tool nuget:?package=OutWit.Common&version=1.2.5
OutWit.Common
OutWit.Common is a zero-dependency, foundational utility library engineered to accelerate the development of high-quality.NET applications.
The Core Philosophy: Non-Intrusive Comparison and Immutable Updates
At the heart of OutWit.Common
lies an elegant solution to a classic dilemma in object-oriented programming with.NET: the conflict between value equality and reference identity.
In standard.NET development, implementing value-based equality requires overriding the Equals()
and GetHashCode()
methods. While this is effective for comparing objects based on their data, it permanently alters their fundamental behavior. An object with a value-based Equals()
implementation can no longer be reliably used as a key in collections like Dictionary<TKey, TValue>
or HashSet<T>
, as its hash code may change if its properties are mutable, leading to lost items and unpredictable behavior.
OutWit.Common
"outwits" this problem by decoupling value comparison from object identity, promoting a safer, more predictable, and highly testable programming model.
The ModelBase
and Is()
Solution
The cornerstone of this philosophy is the OutWit.Common.Abstract.ModelBase
abstract class. By inheriting from ModelBase
, developers are required to implement two key methods: Is()
and Clone()
.
The
Is(ModelBase other)
method is designated for performing deep, value-based comparisons.The standard
Equals()
andGetHashCode()
methods are left untouched, preserving their default reference-based behavior, which is ideal for collection management.
This separation of concerns is exposed to the developer through a rich set of extension methods in the OutWit.Common.Values.ValueUtils
class. The primary method, Is()
, provides a unified API for comparing any two objects. A master dispatcher method, Check()
, intelligently routes the comparison to the most appropriate implementation: it will use ModelBase.Is()
for derived models, CollectionUtils.Is()
for collections, and IComparable.CompareTo()
for primitives, all handled automatically.
This design provides the best of both worlds: robust value comparison for business logic and assertions, and stable reference identity for performance-critical collections.
The With()
Method for Fluent, Immutable Updates
Complementing the comparison model, OutWit.Common
encourages an immutable-style approach to object modification through the PropertiesUtils.With()
extension methods. Instead of directly mutating an object's state, which can lead to side effects and complex state management, the With()
method allows for the creation of a new, modified instance in a single, fluent expression.
This is made possible by the Clone()
method contract from ModelBase
. When With()
is called, it first creates a copy of the object via Clone()
. It then applies the specified changes to the clone and returns the new instance, leaving the original object untouched.
The combination of the Is()
and With()
patterns enables a functional-inspired programming paradigm within C#. Domain models can be treated as immutable data records that are compared by value and "updated" by creating new versions. This approach dramatically enhances predictability, simplifies state tracking, improves thread safety, and makes unit testing significantly more robust, as assertions can be made on object states without fear of mutation.
Getting Started
Installation
To add OutWit.Common
to your project, install it via the NuGet Package Manager.
dotnet add package OutWit.Common
Your First Model
Here is a minimal example of creating a domain model that leverages the core features of the library.
using OutWit.Common.Abstract;
using OutWit.Common.Attributes;
using OutWit.Common.Values;
// 1. Inherit from ModelBase
public class Person : ModelBase
{
// This property will be included in the ToString() output
public int Id { get; set; }
// Use a custom name in the output
public string Name { get; set; }
// 2. Implement the 'Is' method for value comparison
public override bool Is(ModelBase modelBase, double tolerance = DEFAULT_TOLERANCE)
{
if (modelBase is not Person other)
return false;
// Use the ValueUtils.Is() extension for safe and correct comparison
return this.Id.Is(other.Id) && this.Name.Is(other.Name);
}
// 3. Implement the 'Clone' method for immutable-style updates
public override ModelBase Clone()
{
// Return a new instance with the same values
return new Person { Id = this.Id, Name = this.Name };
}
}
Practical Usage Examples
The following examples demonstrate how to use the key patterns of OutWit.Common
.
Example 1: Creating and Comparing Models
Using the Person
class defined above, you can perform robust value comparisons without affecting how the objects behave in collections.
using System;
using OutWit.Common.Values; // For the 'Is' extension method
var person1 = new Person { Id = 1, Name = "John Doe" };
var person2 = new Person { Id = 1, Name = "John Doe" }; // Same values, different instance
var person3 = new Person { Id = 2, Name = "Jane Smith" };
// Use the 'Is()' method for value-based comparison
bool areEqual = person1.Is(person2);
bool areDifferent = person1.Is(person3);
Console.WriteLine($"person1.Is(person2): {areEqual}"); // Output: person1.Is(person2): True
Console.WriteLine($"person1.Is(person3): {areDifferent}"); // Output: person1.Is(person3): False
// The default Equals() method still performs reference comparison
Console.WriteLine($"person1.Equals(person2): {person1.Equals(person2)}"); // Output: person1.Equals(person2): False
Example 2: Fluent, Immutable-Style Updates
The With()
extension method provides a clean and safe way to create modified versions of your models.
using System;
using OutWit.Common.Utils; // For the 'With' extension method
var originalPerson = new Person { Id = 1, Name = "John Doe" };
// Create a new person with an updated name using the 'With' method
var updatedPerson = originalPerson.With(p => p.Name, "Johnathan Doe");
// The original object remains unchanged
Console.WriteLine($"Original: {originalPerson}"); // Output: Original: Id: 1, FullName: John Doe
// The new object has the updated value
Console.WriteLine($"Updated: {updatedPerson}"); // Output: Updated: Id: 1, FullName: Johnathan Doe
Example 3: Deep Collection Comparison
CollectionUtils extends the Is()
logic to entire collections, performing a deep, element-by-element comparison.
using System;
using System.Collections.Generic;
using OutWit.Common.Collections; // For the collection 'Is' extension method
var list1 = new List<Person>
{
new Person { Id = 1, Name = "John Doe" },
new Person { Id = 2, Name = "Jane Smith" }
};
var list2 = new List<Person>
{
new Person { Id = 1, Name = "John Doe" },
new Person { Id = 2, Name = "Jane Smith" }
};
// Even though the lists and their items are different instances,
// 'Is()' correctly identifies them as value-equivalent.
bool areListsEqual = list1.Is(list2);
Console.WriteLine($"Lists are equal: {areListsEqual}"); // Output: Lists are equal: True
// Modify an item in one list
list2 = new Person { Id = 3, Name = "Sam Jones" };
bool areListsStillEqual = list1.Is(list2);
Console.WriteLine($"Lists are still equal: {areListsStillEqual}"); // Output: Lists are still equal: False
Example 4: Declarative Logging with ToStringAttribute
The ToStringAttribute
attribute gives you fine-grained control over the output of ToString()
for easy logging and debugging, without writing any boilerplate code.
using System;
using OutWit.Common.Abstract;
using OutWit.Common.Attributes;
public class Product : ModelBase
{
// Use the 'Name' property to set a custom label in the output.
[ToString(Name = "ID")]
public int ProductId { get; set; }
// If 'Name' is omitted, the property's actual name is used.
[ToString]
public string Sku { get; set; }
// Use the 'Format' property for standard.NET string formatting.
[ToString(Format = "X8")]
public decimal Price { get; set; }
// This property is NOT decorated, so it will be ignored by ToString().
public int StockQuantity { get; set; }
// Required ModelBase implementations
public override bool Is(ModelBase other, double tolerance = DEFAULT_TOLERANCE)
{
if (other is not Product p) return false;
return p.ProductId == ProductId && p.Sku == Sku && Math.Abs(p.Price - Price) < (decimal)tolerance;
}
public override ModelBase Clone() => new Product
{ ProductId = this.ProductId, Sku = this.Sku, Price = this.Price, StockQuantity = this.StockQuantity };
}
// --- Usage ---
var product = new Product
{
ProductId = 101,
Sku = "OWC-LIB-01",
Price = 29.99m,
StockQuantity = 500
};
// The ToString() method automatically formats the output based on the attributes.
Console.WriteLine(product.ToString());
// Expected Output:
// ID: 101, Sku: OWC-LIB-01, Price: $29.99
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
-
net6.0
- No dependencies.
-
net7.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (9)
Showing the top 5 NuGet packages that depend on OutWit.Common:
Package | Downloads |
---|---|
OutWit.Common.Aspects
Notify aspect support for MVVM UI and property changed snippets |
|
OutWit.Common.Proxy
Core components for the OutWit static proxy generator. Contains the essential interfaces and attributes needed to define interception logic and use the generated proxies. |
|
OutWit.OneHourAppStore.Loader
A tool to override running process and load executable from resource |
|
OutWit.InterProcess
Provides the core logic for inter-process communication in the WitRPC framework, enabling .NET applications to spawn external processes and call their services seamlessly as if they were local. |
|
OutWit.Common.Rest
Rest Client Utils. Rest Query Builder |
GitHub repositories
This package is not used by any popular GitHub repositories.