A collection of extension methods for types that implement IEquatable<T> โ concise membership checks (IsInSet / IsNotInSet) and a fluent NotEqual complement to Equals.
dotnet add package Wolfgang.Extensions.IEquatableNuGet Package: Wolfgang.Extensions.IEquatable
This project is licensed under the MIT License. See the LICENSE file for details.
- GitHub Repository: https://github.com/Chris-Wolfgang/IEquatable-Extensions
- API Documentation: https://Chris-Wolfgang.github.io/IEquatable-Extensions/
- CHANGELOG: CHANGELOG.md
- Contributing Guide: CONTRIBUTING.md
using Wolfgang.Extensions.IEquatable;
// Membership checks against a literal set of items
if (status.IsInSet("active", "pending"))
{
// ...
}
if (statusCode.IsNotInSet(200, 201, 204))
{
// ...
}
// Membership against an existing collection
var allowedRoles = new[] { "admin", "editor", "owner" };
if (currentRole.IsInSet(allowedRoles))
{
// ...
}
// Fluent !Equals
if (lhs.NotEqual(rhs))
{
// ...
}All methods are pure: they return a bool and never mutate the input or the
set.
Equality semantics depend on the overload:
- Single-pair overloads (
IsInSet(T t1),IsInSet(T t1, T t2),NotEqual(T), ...) usestatic object.Equals(object, object). That call boxes value types and usesObject.Equalsvirtual dispatch โ it does not prefer the strongly-typedIEquatable<T>.Equals(T)overload. A future revision may switch toEqualityComparer<T>.Defaultto avoid both the boxing and the virtual call (tracked separately). - Collection overloads (
params T[],IEnumerable<T>,ICollection<T>) useEnumerable.Contains/ICollection<T>.Contains, both of which route throughEqualityComparer<T>.Default. That comparer does preferIEquatable<T>.Equals(T)whenTimplements it.
The table below is a snapshot of the public API at the time of writing. For the authoritative list (kept in sync with source on every release), see the API documentation.
| Method | Description |
|---|---|
IsInSet(T t1) |
True when item equals t1. |
IsInSet(T t1, T t2) |
True when item equals any of the two listed values. |
IsInSet(T t1, T t2, T t3) |
True when item equals any of the three listed values. |
IsInSet(params T[] set) |
True when item appears in the provided array. |
IsInSet(IEnumerable<T> set) |
True when item appears in the provided sequence. |
IsInSet(ICollection<T> set) |
True when item appears in the provided collection. Preferred when the call site already has an ICollection<T> โ skips the IEnumerable enumerator boxing path. |
IsNotInSet(...) |
Negation of IsInSet for each of the same overloads. |
NotEqual(T other) |
Fluent !Equals(other) so equality chains read top-to-bottom. |
- Nullable annotations. On
net5.0+(and net8.0/net10.0 builds) all parameters are declaredT?so nullability flows through the call site. Onnet462/netstandard2.0builds โ which predate the framework's nullable reference annotations โ they're declared as plainT. The runtime behaviour is identical; only the compile-time analysis differs. - Collection overload preference. When the source is already an
ICollection<T>, theICollection<T>overload callsICollection<T>.Containsdirectly โ for types likeHashSet<T>that's an O(1) hash lookup. When the source is anIEnumerable<T>the enumerable overload delegates toEnumerable.Contains(LINQ), which is O(N) and routes throughEqualityComparer<T>.Default.
// HTTP status filter
var success = response.StatusCode.IsInSet(200, 201, 204);
// Reverse: anything but a known good code
var problem = response.StatusCode.IsNotInSet(200, 201, 204);
// Role check against a pre-built set
var allowed = new HashSet<string> { "admin", "editor" };
if (user.Role.IsInSet(allowed))
{
// ...
}
// Replace `if (a != b)` with `if (a.NotEqual(b))` when the LHS reads
// awkwardly with a leading negation
if (currentVersion.NotEqual(supportedVersion))
{
return Outdated();
}| Framework | Supported |
|---|---|
| .NET Framework 4.6.2 | โ |
| .NET Standard 2.0 | โ |
| .NET 8.0 | โ |
| .NET 10.0 | โ |
The package multi-targets to keep the dependency footprint small on the modern runtimes while still supporting legacy .NET Framework consumers.
This project enforces strict code quality standards through 8 specialized analyzers, an <TreatWarningsAsErrors>true</TreatWarningsAsErrors> Release gate, and custom rules:
- Microsoft.CodeAnalysis.NetAnalyzers โ Built-in .NET analyzers for correctness and performance
- Roslynator.Analyzers โ Advanced refactoring and code quality rules
- AsyncFixer โ Async/await best practices and anti-pattern detection
- Microsoft.VisualStudio.Threading.Analyzers โ Thread safety and async patterns
- Microsoft.CodeAnalysis.BannedApiAnalyzers โ Prevents usage of banned APIs (see
BannedSymbols.txt); the async-blocking bans in the shared list are inert here since the library is fully synchronous - Meziantou.Analyzer โ Comprehensive code quality rules
- SonarAnalyzer.CSharp โ Industry-standard code analysis
- Microsoft.CodeAnalysis.PublicApiAnalyzers โ Tracks the public API surface via
PublicAPI.Shipped.txt/PublicAPI.Unshipped.txt; surfaces additions/removals at compile time as a breaking-change review gate
git clone https://github.com/Chris-Wolfgang/IEquatable-Extensions.git
cd IEquatable-Extensions
dotnet restore
dotnet build -c Release
dotnet test -c Release --no-buildRequires the .NET 10.0 SDK (or newer) โ earlier SDKs cannot build the net10.0
target. The tests run against every framework the source supports.
See CONTRIBUTING.md for the development workflow, coding conventions, and PR checklist.