Custom Comparison at the object This question is NOT about passing a Comparison to a Sort.  

The question is how to pass a Comparison to an object for object to object  (1:1) Comparison.   Some users must be able to pass in a Comparison.  They want 2 as want be able to tell when they give different comparison.

The way this solution approaches it is to pass a Comparison delegate to the constructor.

static int CompareMotorPrice(Motorcycle motorcycle1, Motorcycle motorcycle2)
{
    return motorcycle1.Price.CompareTo(motorcycle2.Price);
}
static int CompareMotorSpeed(Motorcycle motorcycle1, Motorcycle motorcycle2)
{
    return motorcycle1.Speed.CompareTo(motorcycle2.Speed);
}
public class Motorcycle : IComparable
{
    public string Name { get; }
    public int Speed { get; set; }
    public int Price { get; set; }
    public Comparison&lt;Motorcycle&gt; CompareDelegate { get; set; }
    public Comparison&lt;Motorcycle&gt; CompareDefault { get; set; }
    public int CompareViaDelegate(Motorcycle motorcycle2)
    {
        if (CompareDelegate == null || motorcycle2 == null)
        {
            throw new ArgumentNullException();
        }
        return (CompareDelegate(this, motorcycle2));
    }
    public int CompareTo(object obj)
    {
        if (obj == null) return 1;

        Motorcycle otherMotorcycle = obj as Motorcycle;
        if (otherMotorcycle != null)
        {
            if (CompareDefault == null)
            {
                return (this.Name.CompareTo(otherMotorcycle.Name));
            }
            else
            {
                return CompareDefault(this, otherMotorcycle);
            }
        }
        else
        {
            throw new ArgumentException("Object is not a otherMotorcycle");
        }
    }
    public Motorcycle(string name, int price, int speed, Comparison&lt;Motorcycle&gt; compareDefault = null, Comparison&lt;Motorcycle&gt; compareDelegate = null)
    {
        Name = name;
        Price = price;
        Speed = speed;
        CompareDelegate = compareDelegate;
        CompareDefault = compareDefault;
    }
}


test: 

Motorcycle motorcycle1 = new Motorcycle("harley", 1000, 120, CompareMotorSpeed, CompareMotorPrice);
Motorcycle motorcycle2 = new Motorcycle("yamaha", 1900, 130, CompareMotorSpeed, CompareMotorPrice);
Motorcycle motorcycle3 = new Motorcycle("susuki", 1800, 150, CompareMotorSpeed, CompareMotorPrice);
Debug.WriteLine(motorcycle1.CompareViaDelegate(motorcycle2));
List&lt;Motorcycle&gt; Motorcycles = new List&lt;Motorcycle&gt;() { motorcycle1, motorcycle2, motorcycle3 };
foreach (Motorcycle m in Motorcycles)
{
    Debug.WriteLine($"name {m.Name}   price {m.Price}   speed {m.Speed}");
}
Motorcycles.Sort(CompareMotorPrice);
foreach (Motorcycle m in Motorcycles)
{
    Debug.WriteLine($"name {m.Name}   price {m.Price}   speed {m.Speed}");
}
Motorcycles.Sort();
foreach (Motorcycle m in Motorcycles)
{
    Debug.WriteLine($"name {m.Name}   price {m.Price}   speed {m.Speed}");
}
1) 


    if (CompareDelegate == null || motorcycle2 == null)
    {
        throw new ArgumentNullException();
    }



This exception is misleading. First, it does not specify which parameter is null. Second, strictly speaking CompareDelegate is not a parameter. 

2) I would set the default value for CompareDefault:

public Comparison&lt;Motorcycle&gt; CompareDefault { get; set; } = 
    other =&gt; Name.CompareTo(other.Name);


this way you can probably remove some of the null checks and simplify CompareTo implementation.

3) Overall, I don't like this approach.


it scales poorly.
it hides comparison logic: there is no way to learn how two objects are going to be compared just by reading the code, the comparison logic is hidden inside some delegate.
most importantly, you break asymmetric property of comparison. If a.CompareTo(b) &gt; 0 then I expect that b.CompareTo(a) &lt; 0. But it is not the case, since nothing in your code prevents two objects from using different delegates for comparison. This alone looks like a huge source of problems to me.


IMHO, just write a bunch of regular IComparer&lt;T&gt; implementations and be done with it. It has none of the problems I mentioned above:

//custom comparers are implemented as stand alone classes, 
//they are not part of Motorcycle class.
public class Motorcycle
{
    public string Name { get; }
    public int Speed { get; set; }
    public int Price { get; set; }

    public Motorcycle(string name, int price, int speed)
    {
        Name = name;
        Price = price;
        Speed = speed;
    }
}

//It also guarantees asymmetry:
//if SpeedComparer.Compare(a,b) &lt; 0 then SpeedComparer.Compare(b,a) &gt; 0 is also true
class SpeedComparer : IComparer&lt;Motorcycle&gt;
{
    public int Compare(Motorcycle first, Motorcycle second)
    {
        //add null checks

        return first.Price.CompareTo(second.Price);
    }
}

class NameComparer : IComparer&lt;Motorcycle&gt;
{
    public int Compare(Motorcycle first, Motorcycle second)
    {
        //add null checks

        return first.Name.CompareTo(other.Name);
    }
}


Usage:

public void Test()
{
    var motorcycle1 = new Motorcycle("harley", 1000, 120);
    var motorcycle2 = new Motorcycle("yamaha", 1900, 130);
    var motorcycle3 = new Motorcycle("susuki", 1800, 150);
    var motorcycles = new List&lt;Motorcycle&gt;() { motorcycle1, motorcycle2, motorcycle3 };

    IComparer&lt;Motorcycle&gt; selectedComparer = new NameComparer();
    //compares by name
    TestComparison();

    selectedComparer = new SpeedComparer();
    //selectedComparer is changed, so now speeds are compared
    TestComparison();

    void TestComparison()
    {
        Debug.WriteLine($"Using {selectedComparer.GetType().Name}...");
        Debug.WriteLine(selectedComparer.Compare(motorcycle1, motorcycle2));
        motorcycles.Sort(selectedComparer);
        motorcycles.ForEach(m =&gt; Debug.WriteLine($"name {m.Name}   price {m.Price}   speed {m.Speed}"));
    }
}