Properties
For the sake of simplicity let's assume your view model's properties are defined like this:
public int? PriorityScore { get; set; }
public string MName { get; set; }
public string CName { get; set; }
public string FName { get; set; }
Compare
Your Compare
method can be simply implemented with the following one-liner:
private const int SamePositionInOrdering = 0;
public int Compare(DataAccessViewModel lhs, DataAccessViewModel rhs)
=> lhs is object && rhs is object ? lhs.CompareTo(rhs) : SamePositionInOrdering;
lhs is object
is the new way in C# 9 to express lhs
is not null
- I've used left and right hand side as parameter names, because I think they are more expressive than
x
and y
- You might also consider to make it
static
since it does not rely on instance members
CompareTo
Here I make a separation of the PriorityScore
and the rest of the properties. The reasoning is simple in case of PriorityScore
you don't have fallback value if it is null
.
First lets define an array with property selectors to specify the ordering of MName
, CName
and FName
:
private readonly Func<DataAccessViewModel, IComparable>[] propertySelectors;
public DataAccessViewModel()
{
propertySelectors = new Func<DataAccessViewModel, IComparable>[] {
vm => vm.MName,
vm => vm.CName,
vm => vm.FName,
};
}
- If you unfamiliar with the property selector concept then please visit this SO topic
- I've used
IComparable
instead of string
or object
, since we only care about the CompareTo
method
- This concept is generic enough to support other property types as well, like
int
Then let's see how can we implement the CompareTo
:
private const string FallbackValue = "zzzzzzzzzzzzz";
public int CompareTo(DataAccessViewModel that)
{
int positionInOrdering = (int)(PriorityScore?.CompareTo(that.PriorityScore));
if (positionInOrdering != SamePositionInOrdering) return positionInOrdering;
foreach (var selector in propertySelectors)
{
var lhs = selector(this) ?? FallbackValue;
var rhs = selector(that) ?? FallbackValue;
positionInOrdering = lhs.CompareTo(rhs);
if (positionInOrdering != SamePositionInOrdering) return positionInOrdering;
}
return SamePositionInOrdering;
}
- I've renamed your
mod
parameter to that
, since we are comparing this and that :)
- I've also renamed your
retval
to positionInOrdering
since it better expresses the intent IMHO
- As I said I've treated the
PriorityScore
differently because there is no fallback value for that if its value is null
- I've introduced some constants so the implementation is free from hard coded values.
For the sake of completeness here is the entire implementation of DataAccessViewModel
class
public class DataAccessViewModel: IComparable<DataAccessViewModel>
{
public int? PriorityScore { get; set; }
public string MName { get; set; }
public string CName { get; set; }
public string FName { get; set; }
private const string FallbackValue = "zzzzzzzzzzzzz";
private const int SamePositionInOrdering = 0;
private readonly Func<DataAccessViewModel, IComparable>[] propertySelectors;
public DataAccessViewModel()
{
propertySelectors = new Func<DataAccessViewModel, IComparable>[] {
vm => vm.MName,
vm => vm.CName,
vm => vm.FName,
};
}
public int Compare(DataAccessViewModel lhs, DataAccessViewModel rhs)
=> lhs is object && rhs is object ? lhs.CompareTo(rhs) : SamePositionInOrdering;
public int CompareTo(DataAccessViewModel that)
{
int positionInOrdering = (int)(PriorityScore?.CompareTo(that.PriorityScore));
if (positionInOrdering != SamePositionInOrdering) return positionInOrdering;
foreach (var selector in propertySelectors)
{
var lhs = selector(this) ?? FallbackValue;
var rhs = selector(that) ?? FallbackValue;
positionInOrdering = lhs.CompareTo(rhs);
if (positionInOrdering != SamePositionInOrdering) return positionInOrdering;
}
return SamePositionInOrdering;
}
}
I've used the following data for testing:
List<DataAccessViewModel> models = new()
{
new() { PriorityScore = 1, MName = "M", CName = "C", FName = "F" },
new() { PriorityScore = 1, MName = "M", CName = "C", FName = "F" },
new() { PriorityScore = 0, MName = "M", CName = "C", FName = "F" },
new() { PriorityScore = 2, MName = "N", CName = "C", FName = "F" },
new() { PriorityScore = 2, MName = "M", CName = "C", FName = "F" },
new() { PriorityScore = 4, MName = "M", CName = "C", FName = "G" },
new() { PriorityScore = 4, MName = "M", CName = "C", FName = "F" },
new() { PriorityScore = 3, MName = "M", CName = "D", FName = "F" },
new() { PriorityScore = 3, MName = "M", CName = "C", FName = "F" },
};
models.Sort();
foreach (var model in models)
{
Console.WriteLine($"{{ PriorityScore = {model.PriorityScore}, MName = {model.MName}, CName = {model.CName}, FName = {model.FName} }}");
}
The output is the following:
{ PriorityScore = 0, MName = M, CName = C, FName = F }
{ PriorityScore = 1, MName = M, CName = C, FName = F }
{ PriorityScore = 1, MName = M, CName = C, FName = F }
{ PriorityScore = 2, MName = M, CName = C, FName = F }
{ PriorityScore = 2, MName = N, CName = C, FName = F }
{ PriorityScore = 3, MName = M, CName = C, FName = F }
{ PriorityScore = 3, MName = M, CName = D, FName = F }
{ PriorityScore = 4, MName = M, CName = C, FName = F }
{ PriorityScore = 4, MName = M, CName = C, FName = G }
DataAccessViewModel
class. \$\endgroup\$