C# using语句更深入的理解

5

有人能帮我理解以下代码吗?

public Font MyFont { get; set; }

void AssignFont()
{
    using (Font f = new Font("Shyam",2))
    {
        this.MyFont = f;
    }
}

将一个正在被处理的对象分配给MyFont属性是有效的吗?


我见过这个工作!不过我一直想要一个解释。 - bit
1
“MyFont” 属性实际上是如何使用的?处置仍被引用的对象不仅没问题,而且这是正常情况。如果您没有对它的引用,您将无法处置它!那么,在此代码执行后发生了什么特定的事情,您认为它不应该工作但实际上却可以工作? - Peter Duniho
谢谢。我现在明白了,我应该在使用完MyFont后调用Dispose。我将在我的控件的Dispose方法中处理这个属性。这里是来自Microsoft的解释:http://msdn.microsoft.com/en-us/library/system.drawing.font.dispose.aspx - shyam_
1
理想情况下,我们不应该引用已释放的对象,以便让垃圾收集器对它们进行回收。那么,这种使用方式是错误的吗? - bit
@bit:保留对已释放对象的引用本身并没有什么问题。您应该消除对不再使用的对象的引用,但这与对象是否实现了“IDisposable”以及对象是否已被处理直接无关,除非通常情况下,当您处理“IDisposable”对象时,这表示您已经完成了使用该对象的意图。OP实际上在这里询问的是完全不清楚的。根据最近的评论,听起来他们只是不确定何时处理对象。 - Peter Duniho
注意:在我的理解中,原始问题已经被编辑弄乱了(可以理解)。我认为所提出的问题应该是“将一个将要被处理的对象分配给MyFont属性是否有效?”不过我会等待提问者编辑问题以澄清。 - Peter Duniho
2个回答

10
虽然将一个已释放的对象分配给MyFont属性可能是“有效的”,但该对象可能不再有用,因为它可能已经释放了托管和/或非托管资源。在using语句块开头实例化的对象表示对象的基础类实现了IDisposable接口。这应视为一个警告信号,当对象被释放时,你应该停止与它进行交互,包括保留对它的引用。
在字体的情况下,当你退出using块时,字体的底层资源已被释放:
using (Font f = new Font("Shyam",2))
{
    this.MyFont = f;
}

如果您尝试在任何绘图操作中使用该字体,就可以轻松证明这一点。

如果该字体已被处理,以下代码将失败并出现System.ArgumentException

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }

    private void UserControl1_Load(object sender, EventArgs e)
    {
        if (DesignMode)
        {
            return;
        }

        AssignFont();
    }

    #region Overrides of Control

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.Paint"/> event.
    /// </summary>
    /// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data. </param>
    protected override void OnPaint(PaintEventArgs e)
    {
        try
        {
            var g = e.Graphics;

            g.FillRectangle(Brushes.White, e.ClipRectangle);

            g.DrawString("Hi there", MyFont, Brushes.Black, 0, 0); // <--- this will fail
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.Message);
        }
    }

    #endregion

    void AssignFont()
    {
        using (Font f = new Font("Shyam", 2))
        {
            this.MyFont = f;
        } // <---- MyFont now points to a disposed object
    }

    public Font MyFont { get; set; }
}

enter image description here

你的代码问题在于你在一个使用块中分配了某些内容,并将其引用到其他地方。在你的情况下,因为你想要在其他地方使用这个字体,所以使用 using 块是没有意义的。

错误示范:

using (Font f = new Font("Shyam",2))
{
    this.MyFont = f;
}

更好的:

this.MyFont = new Font("Shyam",2)

我怀疑字体使用本地字体,因此需要资源。


你说得对。谢谢详细的解释,真的很有帮助。 - shyam_

4
通用指南:不要在using块之外使用一次性对象。大多数情况下,一次性对象在被处理后就无法继续使用了。如果您遵循这个规则,您会少遇到麻烦。
在这种特殊情况下,我不建议使用using,因为您希望在AssignFont方法之外使用字体对象。
那么,何时处理字体呢?一种方法是,在父控件的Disposed事件上处理字体(假设this是一个控件)。但是,除非您的应用程序生命周期内生成了成千上万个字体,否则您很可能根本不需要处理字体。如果您只有5-10个字体,则处理它们不应该成为一个大问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接