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
OrderBy
callsCompareTo
on myUserSortable<T>
.CompareTo
calls two callbacks that sets eachPictureBox
so the user can see them.CompareTo
then waits until the user makes a selection. ThePictureBox.Click
event handler tells something like aCancellationTokenSource
to stop waiting.- Which
PictureBox
was 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