OutWit.Common 1.2.5

dotnet add package OutWit.Common --version 1.2.5
                    
NuGet\Install-Package OutWit.Common -Version 1.2.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OutWit.Common" Version="1.2.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OutWit.Common" Version="1.2.5" />
                    
Directory.Packages.props
<PackageReference Include="OutWit.Common" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OutWit.Common --version 1.2.5
                    
#r "nuget: OutWit.Common, 1.2.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OutWit.Common@1.2.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OutWit.Common&version=1.2.5
                    
Install as a Cake Addin
#tool nuget:?package=OutWit.Common&version=1.2.5
                    
Install as a Cake Tool

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() 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.

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .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.

Version Downloads Last Updated
1.2.5 125 8/15/2025
1.2.4 96 8/15/2025
1.2.3 132 8/1/2025
1.2.2 497 7/22/2025
1.2.1 451 6/28/2025
1.2.0 97 6/28/2025
1.1.1 476 6/6/2025
1.1.0 115 6/6/2025
1.0.3 174 3/14/2025
1.0.2 1,451 10/13/2024