ILLightenComparer is a flexible library that can generate very effective and comprehensive IComparer<T> and IEqualityComparer<T> implementations on runtime using advantages of IL code emission.
IEnumerable<T>, T[], T[][]).var comparer = ComparerBuilder.Default.GetComparer<Tuple<int, string>>();
var compareResult = comparer.Compare(x, y);
var equalityComparer = ComparerBuilder.Default.GetEqualityComparer<Tuple<int, string>>();
var equalityResult = equalityComparer.Equals(x, y);
var hashResult = equalityComparer.GetHashCode(x);
And it “just works”, no need complex configuration.
var x = new[] { 1, 2, 3 };
var y = new[] { 2, 3, 1 };
var comparer = new ComparerBuilder()
    .For<int[]>(c => c.IgnoreCollectionsOrder(true))
    .GetComparer();
var result = comparer.Compare(x, y);
result.Should().Be(0);
var x = new Tuple<int, string, double>(1, "value 1", 1.1);
var y = new Tuple<int, string, double>(1, "value 2", 2.2);
var comparer = new ComparerBuilder()
    .For<Tuple<int, string, double>>()
    .Configure(c => c.IgnoreMember(o => o.Item2)
                     .IgnoreMember(o => o.Item3))
    .GetComparer();
var result = comparer.Compare(x, y);
result.Should().Be(0);
var x = _fixture.Create<Tuple<int, string>>();
var y = _fixture.Create<Tuple<int, string>>();
var customComparer = new CustomizableComparer<Tuple<int, string>>((a, b) => 0); // makes all objects always equal
var comparer = new ComparerBuilder()
    .Configure(c => c.SetCustomComparer(customComparer))
    .GetComparer<Tuple<int, string>>();
var result = comparer.Compare(x, y);
result.Should().Be(0);
var builder = new ComparerBuilder(c => c.SetDefaultCyclesDetection(false)); // defines initial configuration
// adds some configuration later
builder.Configure(c => c
    .SetStringComparisonType(
        typeof(Tuple<int, string, Tuple<short, string>>),
        StringComparison.InvariantCultureIgnoreCase)
    .IgnoreMember<Tuple<int, string, Tuple<short, string>>, int>(o => o.Item1));
// defines configuration for specific types
builder.For<Tuple<short, string>>(c => c.DefineMembersOrder(
    order => order.Member(o => o.Item2)
                  .Member(o => o.Item2)));
// adds additional configuration to existing configuration
builder.For<Tuple<int, string, Tuple<short, string>>>(c => c.IncludeFields(false));
var x = new Tuple<int, string>(1, "text");
var y = new Tuple<int, string>(2, "TEXT");
// initially configuration defines case insensitive string comparison
var builder = new ComparerBuilder()
    .For<Tuple<int, string>>(c => c
        .SetStringComparisonType(StringComparison.CurrentCultureIgnoreCase)
        .DetectCycles(false));
// in addition, setup to ignore first member
builder.Configure(c => c.IgnoreMember(o => o.Item1));
// this version takes in account only case insensitive second member
var ignoreCaseComparer = builder.GetComparer();
// override string comparison type with case sensitive setting and build new comparer
var originalCaseComparer = builder
    .For<Tuple<int, string>>()
    .Configure(c => c.SetStringComparisonType(StringComparison.Ordinal))
    .GetComparer();
// first comparer ignores case for strings still
ignoreCaseComparer.Compare(x, y).Should().Be(0);
// second comparer still ignores first member but uses new string comparison type
var result = originalCaseComparer.Compare(x, y);
result.Should().Be(string.Compare("text", "TEXT", StringComparison.Ordinal));
sealed classes and small types (sbyte, byte, char, short, ushort) when possible.IComparable<T> interface then this implementations will be used.In case of an unexpected behavior, please welcome to create an issue and provide the type and data that you use.