![]() |
VOOZH | about |
dotnet add package OutWit.Common --version 1.4.0
NuGet\Install-Package OutWit.Common -Version 1.4.0
<PackageReference Include="OutWit.Common" Version="1.4.0" />
<PackageVersion Include="OutWit.Common" Version="1.4.0" />Directory.Packages.props
<PackageReference Include="OutWit.Common" />Project file
paket add OutWit.Common --version 1.4.0
#r "nuget: OutWit.Common, 1.4.0"
#:package OutWit.Common@1.4.0
#addin nuget:?package=OutWit.Common&version=1.4.0Install as a Cake Addin
#tool nuget:?package=OutWit.Common&version=1.4.0Install as a Cake Tool
OutWit.Common is a zero-dependency, foundational utility library engineered to accelerate the development of high-quality.NET applications.
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.
ModelBase and Is() SolutionThe 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() and GetHashCode() 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.
With() Method for Fluent, Immutable UpdatesComplementing 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.
To add OutWit.Common to your project, install it via the NuGet Package Manager.
dotnet add package OutWit.Common
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 };
}
}
The following examples demonstrate how to use the key patterns of OutWit.Common.
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
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
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
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
Licensed under the Apache License, Version 2.0. See LICENSE.
If you use OutWit.Common in a product, a mention is appreciated (but not required), for example: "Powered by OutWit.Common (https://ratner.io/)";
"OutWit" and the OutWit logo are used to identify the official project by Dmitry Ratner.
You may:
You may not:
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 is compatible. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 is compatible. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 was computed. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
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.Common.Plugins.Abstractions
Provides a lightweight set of abstractions for creating plugins for the OutWit.Common.Plugins framework. |
|
|
OutWit.Common.Plugins
A robust and flexible plugin system for .NET. Features dynamic discovery from directories, sophisticated dependency resolution (validates versions and detects circular dependencies), and isolated loading via AssemblyLoadContext to enable hot-reloading and unloading of plugins. |
|
|
OutWit.OneHourAppStore.Loader
A tool to override running process and load executable from resource |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.4.0 | 521 | 5/24/2026 |
| 1.3.2 | 6,006 | 1/25/2026 |
| 1.3.1 | 1,615 | 11/23/2025 |
| 1.3.0 | 1,772 | 11/12/2025 |
| 1.2.5 | 353 | 8/15/2025 |
| 1.2.4 | 190 | 8/15/2025 |
| 1.2.3 | 286 | 8/1/2025 |
| 1.2.2 | 609 | 7/22/2025 |
| 1.2.1 | 1,114 | 6/28/2025 |
| 1.2.0 | 177 | 6/28/2025 |
| 1.1.1 | 877 | 6/6/2025 |
| 1.1.0 | 192 | 6/6/2025 |
| 1.0.3 | 296 | 3/14/2025 |
| 1.0.2 | 2,410 | 10/13/2024 |