Object comparison method as an example of using the functional style
Functional and OO mix

This is kind of a mix of functional and OO programming because the class is carring a state. In a pure functional way you would provide the method with all it needs this means it would require the generic &lt;T&gt; parameter and it would be an extension:

public static IEnumerable&lt;DifferenceBag&gt; CompareTo&lt;T&gt;(this T source, T target)
{
    // perform the T reflection etc
}


or with pre-reflection you could have another function that takes a collection of properties that you want to compare:

public static IEnumerable&lt;DifferenceBag&gt; CompareTo&lt;T&gt;(this T source, T target, IEnumerable&lt;PropertyInfo&gt; properties)
{
    // perform the T reflection outside
}


this means you could later jus say:

var differences = foo.CompareTo(bar);


or

var comparableProperties = typeof(T).GetComparableProperties();
var differences = foo.CompareTo(bar, comparableProperties);


or even

var differences = foo.CompareTo(bar, GetComparableProperties);


if you designed the CompareTo method in such a way that it can take a Func for the reflection part:

public static IEnumerable&lt;DifferenceBag&gt; CompareTo&lt;T&gt;(this T source, T target, Func&lt;T, IEnumerable&lt;PropertyInfo&gt;&gt; getProperties)
{
    // perform the T reflection outside
}


where 

static IEnumerable&lt;PropertyInfo&gt; GetComparableProperties&lt;T&gt;() {}




Implementation

You could improve the current implementatio to be more functional by using more LINQ. The Compare method could work like this

var differences =
    from p in _properties       
    let pair = (left: p.GetValue(source), right: p.GetValue(target))
    where !pair.left.Equals(pair.right)
    select new DifferenceBag(p.Name, pair.left, pair.right, p);





if (_properties.Count == 0)
  return EmptyBag;



These lines are unnecessary because if there are not properties the resulting collection will be empty anwyway.



The extreme C# 7 solution could be implemented like this

public delegate IEnumerable&lt;PropertyInfo&gt; GetPropertiesCallback&lt;T&gt;();

public static IEnumerable&lt;DifferenceBag&gt; CompareTo&lt;T&gt;(
    this T source,
    T target,
    GetPropertiesCallback&lt;T&gt; getProperties)
    where T : class // required if you want to use "?? throw"
{
    var differences =
        from p in getProperties()
        let pair = (
            left: p.GetValue(source ?? throw new ArgumentNullException(nameof(source))),
            right: p.GetValue(target ?? throw new ArgumentNullException(nameof(source)))
        )
        where !pair.left.Equals(pair.right)
        select new DifferenceBag(p.Name, pair.left, pair.right, p);

    return differences;
}




Possible bug


var immutable = propertySet[i].GetCustomAttribute&lt;ImmutableAttribute&gt;();

if (immutable != null &amp;&amp; immutable.IsTargetMutable)
{
    builder.Add(propertySet[i]);
}

var ignore = propertySet[i].GetCustomAttribute&lt;IgnoreCompareAttribute&gt;();

if (ignore != null &amp;&amp; !ignore.SkipProperty)
{
    builder.Add(propertySet[i]);
}



If a property is decorated with both attributes you'll end up having it twice in the final list.