我正在寫一個小應用程序,幫助我按照主觀質量排序圖片。我對實現機器學習解決方案不感興趣,因此我決定採用用戶指導的排序方式,向用戶呈現圖片對,並且需要選擇哪一對是更好的圖片(或者如果他們兩者都具有相同的質量)。IComparable.CompareTo - 實現異步比較,等待按鈕按下
而不是試圖拆分類似快速排序或插入排序的東西,我決定去現有的OrderBy
LINQ方法,該方法在實現IComparable<T>
的對象上工作。所需的代碼路徑如下
OrderBy
我UserSortable<T>
呼籲CompareTo
。CompareTo
調用兩個回調,設置每個PictureBox
以便用戶可以看到它們。CompareTo
然後等待,直到用戶做出選擇。PictureBox.Click
事件處理程序告訴類似CancellationTokenSource
停止等待。- 哪個
PictureBox
被點擊存儲爲UserSortable<T>
所引用的表單中的一個屬性 - 將其值作爲CompareTo
的結果進行檢查和使用。
的問題,截至目前,在步驟3 CancellationTokenSource
不支持取消從窗體的單擊事件,但它拋出一個TaskCanceledException
,我嘗試實際得到的結果。
此外,因爲int IComparable<T>.CompareTo(T)
是一個現有的接口,我不能等待任何來自它的調用,導致我做了hacking的GetAwaiter().GetResult()
方法。
什麼是最好的方式去實現呢?
代碼:UserSortable<T>
internal sealed class UserSortable<T> : IComparable<UserSortable<T>>
{
private UserDirectedSortForm form;
private Action<T> setFirstItem;
private Action<T> setSecondItem;
private CancellationTokenSource cancellationToken;
internal T Item { get; private set; }
public UserSortable(UserDirectedSortForm form, T item, Action<T> setFirstItem, Action<T> setSecondItem)
{
this.form = form;
this.Item = item;
this.setFirstItem = setFirstItem;
this.setSecondItem = setSecondItem;
}
public async Task CompareTo(UserSortable<T> other)
{
setFirstItem(Item);
setSecondItem(other.Item);
form.CancellationToken = cancellationToken = new CancellationTokenSource();
await WaitForUserSelection();
}
private async Task<int> AssignResult(UserSortable<T> other)
{
await CompareTo(other);
int result = 0;
if (form.Selected == 'A') { result = -1; }
else if (form.Selected == 'B') { result = 1; }
else if (form.Selected == '=') { result = +1; }
else { result = int.MaxValue; }
return result;
}
private Task WaitForUserSelection()
{
return Task.Delay(-1, cancellationToken.Token);
}
int IComparable<UserSortable<T>>.CompareTo(UserSortable<T> other)
{
return AssignResult(other).GetAwaiter().GetResult();
}
}
UserDirectedSortForm
public partial class UserDirectedSortForm : Form
{
internal CancellationTokenSource CancellationToken { get; set; }
private List<UserSortable<int>> items;
internal char Selected { get; set; }
public UserDirectedSortForm()
{
InitializeComponent();
int first = 0;
int second = 0;
Action<int> setFirst = i => first = i;
Action<int> setSecond = i =>
{
second = i;
string newText = $"Which is larger? {first} or {second}?";
Invoke((MethodInvoker)delegate { label1.Text = newText; });
};
items = new List<UserSortable<int>>();
items.Add(new UserSortable<int>(this, 0, setFirst, setSecond));
items.Add(new UserSortable<int>(this, 4, setFirst, setSecond));
items.Add(new UserSortable<int>(this, 12, setFirst, setSecond));
items.Add(new UserSortable<int>(this, 3, setFirst, setSecond));
items.Add(new UserSortable<int>(this, 13, setFirst, setSecond));
items.Add(new UserSortable<int>(this, 4, setFirst, setSecond));
items.Add(new UserSortable<int>(this, 26, setFirst, setSecond));
items.Add(new UserSortable<int>(this, 543221, setFirst, setSecond));
}
private void AssignResult(char result)
{
Selected = result;
try
{
using (CancellationToken)
{
CancellationToken.Cancel();
}
CancellationToken = null;
}
catch (TaskCanceledException ex)
{
;
}
}
private void button1_Click(object sender, EventArgs e)
{
AssignResult('A');
}
private void button2_Click(object sender, EventArgs e)
{
AssignResult('B');
}
private void button3_Click(object sender, EventArgs e)
{
AssignResult('=');
}
private void label1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
StringBuilder builder = new StringBuilder();
foreach (var item in items.OrderBy(i => i))
{
builder.Append(item.Item);
builder.Append(", ");
}
MessageBox.Show(builder.ToString());
});
}
}
看起來你的表單是在原始線程上創建的,但是後來在'AssignResult()'的後臺線程中使用。我不確定我會怎樣解決這個問題,但我確實知道WinForms控件只能在創建它們的同一個線程上使用。 – Andy
雖然我不確定如何在UI線程上進行等待按鈕比較而不會阻止它。 –
我不認爲在這裏使用'OrderBy'是一個特別好的主意。人類是人類,它可以切實發生,「我(法師)1」比「I2」更好,「I3」比「I2」更好,但「I1」可能比「更好」 'I3',從而打破比較邏輯 –