Nie zakładać ślad stosu wyjątku, ale spodziewam się, że wyglądało to mniej więcej tak:
System.InvalidOperationException: Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.set_WindowText(String value)
at System.Windows.Forms.TextBoxBase.set_WindowText(String value)
at System.Windows.Forms.Control.set_Text(String value)
at System.Windows.Forms.TextBoxBase.set_Text(String value)
at System.Windows.Forms.TextBox.set_Text(String value)
at WindowsFormsApplicationcSharp2015.Form1.<.ctor>b__0_0() in D:\test\WindowsFormsApplicationcSharp2015\Form1.cs:line 27
widzimy, że jest wyjątek od nieruchomości Control.Handle
pochłaniacza. I rzeczywiście, jeśli spojrzymy na source code dla tej nieruchomości nie jest, zgodnie z oczekiwaniami:
public IntPtr Handle {
get {
if (checkForIllegalCrossThreadCalls &&
!inCrossThreadSafeCall &&
InvokeRequired) {
throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,
Name));
}
if (!IsHandleCreated)
{
CreateHandle();
}
return HandleInternal;
}
}
Interesującą częścią jest, gdy spojrzymy na kod, który wywołuje Control.Handle
. W tym przypadku jest to właściwość Control.WindowText seter:
set {
if (value == null) value = "";
if (!WindowText.Equals(value)) {
if (IsHandleCreated) {
UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value);
}
else {
if (value.Length == 0) {
text = null;
}
else {
text = value;
}
}
}
}
Zauważ, że własność Handle
jest wywoływana tylko wtedy IsHandleCreated
jest true
.
I pod względem kompletności, jeśli spojrzymy na kod dla IsHandleCreated widzimy następujące:
public bool IsHandleCreated {
get { return window.Handle != IntPtr.Zero; }
}
Więc powód nie dostaniesz wyjątek, dlatego do czasu Task
zostanie uruchomiony, uchwyt okna nie został jeszcze utworzony, czego można się spodziewać, ponieważ Task
rozpoczyna się w konstruktorze formularza, czyli zanim formularz zostanie wyświetlony.
Przed utworzeniem uchwytu okna modyfikowanie właściwości nie wymaga jeszcze pracy z wątku interfejsu użytkownika. W tym małym oknie czasowym na początku programu wydaje się, że możliwe jest wywoływanie metod na instancjach sterujących z wątku innego niż UI bez uzyskania wyjątku "krzyżowego". Jasne jest jednak, że istnienie tego specjalnego, małego okna czasowego nie zmienia faktu, że zawsze powinniśmy upewnić się, że metody kontrolne z wątku UI są bezpieczne.
Aby udowodnić, że decydującym czynnikiem w uzyskaniu (lub nie) wyjątku "krzyżowego" jest próba utworzenia uchwytu okna, spróbuj zmodyfikować przykład, aby wymusić utworzenie uchwytu okna przed rozpoczęciem zadania i zauważyć, jak będzie teraz konsekwentnie uzyskać oczekiwaną wyjątku, nawet bez snu:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Force creation of window handle
var dummy = txtHello.Handle;
Task.Run(() =>
{
txtHello.Text = "Hello"; // kaboom
});
}
}
stosownej dokumentacji: Control.Handle
Jeśli uchwyt nie został jeszcze stworzony, przedstawieniu nieruchomość zmusi uchwyt do utworzenia.
To będzie dla mnie niespodzianka, jeśli to prawda! –