I'm writing a small app that helps me sort pictures in order of their subjective quality. I'm not interested in implementing a machine learning solution, so I decided to go with a user-directed sort in which the user is presented with pairs of pictures and is required to select which of the pair is the better picture (or if they're both at the same quality).
Instead of trying to split something like quicksort or insertion sort in half, I decided to go with the existing OrderBy LINQ method, which works on objects implementing IComparable<T>. The desired code path is as follows
OrderBycallsCompareToon myUserSortable<T>.CompareTocalls two callbacks that sets eachPictureBoxso the user can see them.CompareTothen waits until the user makes a selection. ThePictureBox.Clickevent handler tells something like aCancellationTokenSourceto stop waiting.- Which
PictureBoxwas clicked is stored as a property on the form that theUserSortable<T>has a reference to - its value is checked and used as the result ofCompareTo.
The problem, as of now, is in step 3. CancellationTokenSource does support cancellation from the form's click event, but it throws a TaskCanceledException as I try to actually get the result.
Additionally, because int IComparable<T>.CompareTo(T) is an existing interface, I cannot await call anything from it, leading me to do the hackish GetAwaiter().GetResult() method.
What's the best way to go about implementing this?
Code: 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());
});
}
}
Aucun commentaire:
Enregistrer un commentaire