2016-06-10 47 views
1

我正在寫一個小應用程序,幫助我按照主觀質量排序圖片。我對實現機器學習解決方案不感興趣,因此我決定採用用戶指導的排序方式,向用戶呈現圖片對,並且需要選擇哪一對是更好的圖片(或者如果他們兩者都具有相同的質量)。IComparable.CompareTo - 實現異步比較,等待按鈕按下

而不是試圖拆分類似快速排序或插入排序的東西,我決定去現有的OrderBy LINQ方法,該方法在實現IComparable<T>的對象上工作。所需的代碼路徑如下

  1. OrderByUserSortable<T>呼籲CompareTo
  2. CompareTo調用兩個回調,設置每個PictureBox以便用戶可以看到它們。
  3. CompareTo然後等待,直到用戶做出選擇。 PictureBox.Click事件處理程序告訴類似CancellationTokenSource停止等待。
  4. 哪個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()); 
      }); 
     } 
    } 
+0

看起來你的表單是在原始線程上創建的,但是後來在'AssignResult()'的後臺線程中使用。我不確定我會怎樣解決這個問題,但我確實知道WinForms控件只能在創建它們的同一個線程上使用。 – Andy

+0

雖然我不確定如何在UI線程上進行等待按鈕比較而不會阻止它。 –

+3

我不認爲在這裏使用'OrderBy'是一個特別好的主意。人類是人類,它可以切實發生,「我(法師)1」比「I2」更好,「I3」比「I2」更好,但「I1」可能比「更好」 'I3',從而打破比較邏輯 –

回答

1

作爲評價由Anton Gogolev提及,用戶控制的各種不能工作,因爲傳遞性(A> B,B> C,因此,一個> C)不能得到保證,因此沒有用戶排序的集合可以具有全部訂單或甚至基於它們相對於其他項目的質量而被指定的數字質量。

我可以強制執行類似二進制插入排序的傳遞性,但這可能仍然是錯誤的(強制傳遞性,如果沒有的話也會導致順序錯誤)。我可能會最終做一個星級評分系統。