最近再kibana的Error Log上遇到了一個List.Sort排序Compare比較器的不一致結果的問題,因為沒有當下發生的錯誤資料,只能靠通靈來推測發生原因,附上相關程式碼和自己抓蟲的過程,希望有看完的大大能給小弟一點方向或是思路。
錯誤訊息如下
Unable to sort because the IComparer.Compare() method returns inconsistent results
下方是我使用比較器的實作方法:
foreach (var group in groups)
{
var list = group.AsEnumerable().ToList();
list.Sort(delegate(CustomObject s1, CustomObject s2)
{
if (string.IsNullOrEmpty(s1.Value) || string.IsNullOrEmpty(s2.Value))
return 0;
var s1Value = GetAverageValue(s1.Value);
var s2Value = GetAverageValue(s2.Value);
if (s1Value > s2Value) return 1;
if (s1Value < s2Value) return -1;
return 0;
});
}
static decimal GetAverageValue(string input)
{
var values = new List<decimal>();
var isNegative = input.Contains('-');
var substrings = input.Split("/");
foreach (var str in substrings)
{
if (!decimal.TryParse(str, out var value)) continue;
if (isNegative && value > 0) value *= -1;
values.Add(value);
}
return values.Average();
}
下方主要是所有會出現的格式我的類別,實際情況可能就是數字有所改變
public class CustomObject
{
public string Value { get; set; }
}
List<CustomObject> list = new List<CustomObject>
{
new CustomObject { Value = "1" },
new CustomObject { Value = "2" },
new CustomObject { Value = "3" },
new CustomObject { Value = "" },
new CustomObject { Value = "-1" },
new CustomObject { Value = "-2" },
new CustomObject { Value = "-3" },
new CustomObject { Value = "0" },
new CustomObject { Value = "-1/2" },
new CustomObject { Value = "-2/3" },
new CustomObject { Value = "-3/4" },
new CustomObject { Value = "1/2" },
new CustomObject { Value = "2/3" },
new CustomObject { Value = "3/4" }
};
追蹤問題過程有去看了一下IL Code和實際Debug偵測
若資料筆數少於16筆會使用插入排序,大於16筆才會進入堆排序,
堆排序的比較過程中才會出現此問題,目前認為可能發生的問題點為使用平均值的地方回傳的結果造成比較器實現不正確,所以先使用簡單的方法在最後比較的值直接加二,我最終有成功最小限度還原錯誤情境,但是原本使用算出平均值的資料錯誤情境還是沒辦法還原出來。
最小限度還原錯誤情境
List<CustomObject> list = new List<CustomObject>
{
new CustomObject { Value = 3 },
new CustomObject { Value = 1 },
new CustomObject { Value = 2 },
new CustomObject { Value = 3 },
new CustomObject { Value = 1 },
new CustomObject { Value = 2 },
new CustomObject { Value = 3 },
new CustomObject { Value = 1 },
new CustomObject { Value = 2 },
new CustomObject { Value = 3 },
new CustomObject { Value = 1 },
new CustomObject { Value = 2 },
new CustomObject { Value = 3 },
new CustomObject { Value = 1 },
new CustomObject { Value = 2 },
new CustomObject { Value = 3 },
new CustomObject { Value = 1 },
new CustomObject { Value = 2 },
};
list.Sort(delegate(CustomObject s1, CustomObject s2)
{
if (s1.Value > s2.Value+2) return 1;
if (s1.Value < s2.Value+2) return -1;
return 0;
});
另外想到可能的情境是因為資料是從MemoryCache取得,會不會有可能是在排序時,MemoryCache的值更新,造成比較錯誤,但是因為Foreach的迴圈裡有用 var list = group.AsEnumerable().ToList();
,有使用ToList應該是不會被延遲執行影響,所以自己打槍了自己,想請教各位大大有麼有其他看法,萬分感謝。
不確定跟這個有沒有關係
public class CustomObject
{
public int
Value { get; set; }
}
List list = new List
{
new CustomObject { Value = "-1/2"
},
new CustomObject { Value = "-2/3"
},
new CustomObject { Value = "-3/4"
},
new CustomObject { Value = "1/2"
},
new CustomObject { Value = "2/3"
},
new CustomObject { Value = "3/4"
}
}
你的比較方法邏輯亂掉了的關係?
不熟C#不過
s1 = 1, s2 = 3
s1 = 3, s2 = 1
跑出來的結果會不一樣
應該是樣導致跳錯?