IComparable.CompareTo - 实现按下按钮时等待的异步比较

时间:2016-06-10 13:49:04

标签: c# winforms linq sorting asynchronous

我正在写一个小应用程序,帮助我按照主观质量排序图片。我对实现机器学习解决方案不感兴趣,所以我决定采用用户指导的排序,在该排序中向用户呈现成对的图片,并且需要选择哪一对是更好的图片(或者如果他们是“两者的质量相同。”

我决定采用现有的OrderBy LINQ方法,而不是尝试将快速排序或插入排序分成两半,该方法适用于实现IComparable<T>的对象。所需的代码路径如下

  1. OrderByCompareTo
  2. 上致电UserSortable<T>
  3. CompareTo调用两个回调设置每个PictureBox,以便用户可以看到它们。
  4. CompareTo然后等待用户做出选择。 PictureBox.Click事件处理程序会让CancellationTokenSource之类的东西停止等待。
  5. 点击了PictureBox的{​​{1}}被存储为UserSortable<T>引用的表单上的属性 - 检查其值并将其用作CompareTo的结果。
  6. 截至目前,问题出在第3步。CancellationTokenSource确实支持从表单的click事件中取消,但在尝试实际获取结果时会抛出TaskCanceledException

    此外,因为int IComparable<T>.CompareTo(T)是一个现有的接口,我无法等待它的任何调用,导致我做了hackish 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());
                });
            }
        }
    

1 个答案:

答案 0 :(得分:1)

正如Anton Gogolev在评论中所提到的,用户指导的排序无法工作,因为传递性(A> B,B> C,因此A> C)无法保证,因此没有用户排序的集合可以具有总订单甚至根据其相对于其他项目的质量分配数字质量。

我可以通过执行二进制插入排序来强制传递,但这可能仍然是错误的(强制传递性,如果没有也会使顺序错误)。我可能最终会做一个星级评分系统。