Long-time joelonsoftware follower, 1st-time stackoverflow poster.
I want to know "how safely" I can do the following (C#):
Form formDlg = new Form();
TextBox box = new TextBox();
formDlg.Controls.Add( box );
formDlg.ShowD开发者_JAVA百科ialog();
formDlg.Dispose();
string sUserEntered = box.Text; // After parent Dispose'd!
In practice, this (apparently) works, because box (as a Control) has a private text field (a string) which it uses to implement its Text property after its window handle is destroyed.
I won't be satisfied by a general answer that "you can't access an object after it's Disposed" because (1) I can't find any such blanket prohibition in MS docs, (2) I'm not accessing an unmanaged resource, and (3) this code doesn't throw any exception (including ObjectDisposedException).
I would like to do this so I can create and use a combined "ShowAndDispose" method to reduce the risk of forgetting to always call Dispose() after ShowDialog().
To complicate, the behavior changes in the debugger. If I break before Dispose(); then Quick Watch box and drill down into its Control base class; then step past Dispose(); then box.Text returns ""! In other scenarios box.Text returns the user-entered text.
It is an implementation detail that this code runs without a problem. The Control.Text property happens to be cached by the Control class so disposing the TextBox doesn't cause an ObjectDisposed exception.
That's fairly rare btw, lots of control property getters and setters generate a Windows message to ask the native Window control for the property value. You'll get a kaboom on those because the Handle property is no longer valid. Notable also is that the Text property setter updates the cached value but also generates a Window message to update the native control. Kaboom here.
I assume this is just general interest, don't ever use code like that in your program. Well, you'd find out quick enough.
You can use the 'using' statement to ensure an object gets disposed when you're done with it:
using(Form frmDialog = new Form())
{
//Do stuff
}
frmDialog will get disposed once the block has run I believe.
The debugger scenario makes me think that what you do is not reliable, to test it you should at least try this:
formDlg.Dispose();
Application.DoEvents();
GC.Collect();
GC.WaitForPendingFinalizers();
string sUserEntered = box.Text; // After parent Dispose'd!
I put the sUserEntered
value into a public property so it could be accessed:
public string UserInput
{
get;
set;
}
public frmDialog()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
//
// TODO: Add constructor code after the InitializeComponent() call.
//
}
void Button1Click(object sender, EventArgs e)
{
UserInput = userInput.Text;
this.Dispose();
}
Then in my mainform:
using (dialog = new frmDialog())
{
dialog.ShowDialog();
stringUserInput.Text = dialog.UserInput;
};
It occurs to me, I can create & use a Form-derived class with a BeginShowDialog() method which calls ShowDialog(), and an EndShowDialog() method which calls Dispose(). The "Begin" in the method name will make the need for the "End" call more obvious.
I miss C++'s determinate destruction of locals on leaving scope.
精彩评论