27

我对以下三种清除文本框内容的方法感到有些困惑。我正在使用 WPF 并发现所有都在工作,但我无法找到差异。

有人可以用一些例子向我解释一下吗?

  • txtUserName.Clear();
  • txtUserName.Text = string.Empty;
  • txtUserName.Text = "";
4

10 回答 10

25

如果不是很深入:

清除:从 TextBox 中删除内容,并可能删除分配给它的资源

    public void Clear()
    {
      using (this.TextSelectionInternal.DeclareChangeBlock())
      {
        this.TextContainer.DeleteContentInternal(this.TextContainer.Start, this.TextContainer.End);
        this.TextSelectionInternal.Select(this.TextContainer.Start, this.TextContainer.Start);
      }
    }

将空字符串(因为 string.Empty 和 "" 相等)分配给 Text 属性只需将空字符串分配给附加属性 TextBox.TextProperty:

public string Text
{
  get
  {
    return (string) this.GetValue(TextBox.TextProperty);
  }
  set
  {
    this.SetValue(TextBox.TextProperty, (object) value);
  }
}
于 2013-08-29T12:55:33.053 回答
19

Clear()方法不仅仅是从TextBox. 它会删除所有内容并重置文本选择和插入符号,因为@syned 的回答很好地表明了这一点。

例如,如果字符串池中尚不存在空对象txtUserName.Text = "";,框架将创建一个空对象并将其设置为属性。但是,如果该字符串已在应用程序中使用,则框架将使用池中的此值。stringText""

例如txtUserName.Text = string.Empty;,框架不会创建一个空string对象,而是引用一个空字符串常量,并将其设置为Text属性。

在性能测试中,已经表明(在 C# 中,我应该使用 string.Empty 还是 String.Empty 还是“”?帖子)后两个示例之间确实没有有用的区别。调用该Clear()方法肯定是最慢的,但这显然是因为它除了清除文本之外还有其他工作要做。即便如此,三个选项之间的性能差异仍然几乎不明显。

于 2013-09-02T10:41:30.810 回答
4

如果您落后于一些性能差异或内存泄漏,则没有太多(在设置文本而不是使用 .Clear() 时只是对事件的一些额外调用)

但是,您在使用 MVVM 时无权控制自身,因此清除文本的唯一方法是使用 TextBox 将文本设置为绑定属性

在标准应用程序中,您可以做任何您想做的事情(我更喜欢使用为此目的设计的 .Clear() 方法)。

于 2013-09-02T15:01:34.960 回答
3

似乎它正在做一些额外的事情,比如检查更改的来源、绑定、更新插入符号位置和更新/清除撤消。分配空字符串时可能不需要其中的大部分。

/// <summary>
/// Callback for changes to the Text property
/// </summary>
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    TextBox textBox = (TextBox)d;
    bool inReentrantChange = false;
    int savedCaretIndex = 0;

    if (textBox._isInsideTextContentChange)
    {
        // Ignore property changes that originate from OnTextContainerChanged,
        // unless they contain a different value (indicating that a
        // re-entrant call changed the value)
        if (textBox._newTextValue != DependencyProperty.UnsetValue)
        {
            // OnTextContainerChanged calls
            //      SetCurrentDeferredValue(TextProperty, deferredTextReference)
            // Usually the DeferredTextReference will appear in the new entry
            if (textBox._newTextValue is DeferredTextReference)
            {
                if (e.NewEntry.IsDeferredReference &&
                    e.NewEntry.IsCoercedWithCurrentValue &&
                    e.NewEntry.ModifiedValue.CoercedValue == textBox._newTextValue)
                {
                    return;
                }
            }
            // but if the Text property is data-bound, the deferred reference
            // gets converted to a real string;  during the conversion (in
            // DeferredTextReference.GetValue), the TextBox updates _newTextValue
            // to be the string.
            else if (e.NewEntry.IsExpression)
            {
                object newValue = e.NewEntry.IsCoercedWithCurrentValue
                                    ? e.NewEntry.ModifiedValue.CoercedValue
                                    : e.NewEntry.ModifiedValue.ExpressionValue;
                if (newValue == textBox._newTextValue)
                {
                    return;
                }
            }
        }

        // If we get this far, we're being called re-entrantly with a value
        // different from the one set by OnTextContainerChanged.  We should
        // honor this new value.
        inReentrantChange = true;
        savedCaretIndex = textBox.CaretIndex;
    }

    // CoerceText will have already converted null -> String.Empty,
    // but our default CoerceValueCallback could be overridden by a
    // derived class.  So check again here.
    string newText = (string)e.NewValue;
    if (newText == null)
    {
        newText = String.Empty;
    }

    textBox._isInsideTextContentChange = true;
    try
    {
        using (textBox.TextSelectionInternal.DeclareChangeBlock())
        {
            // Update the text content with new TextProperty value.
            textBox.TextContainer.DeleteContentInternal((TextPointer)textBox.TextContainer.Start, (TextPointer)textBox.TextContainer.End);
            textBox.TextContainer.End.InsertTextInRun(newText);

            // Collapse selection to the beginning of a text box
            textBox.Select(savedCaretIndex, 0);
        }
    }
    finally
    {
        //
        if (!inReentrantChange)
        {
            textBox._isInsideTextContentChange = false;
        }
    }

    // We need to clear undo stack in case when the value comes from
    // databinding or some other expression.
    if (textBox.HasExpression(textBox.LookupEntry(TextBox.TextProperty.GlobalIndex), TextBox.TextProperty))
    {
        UndoManager undoManager = textBox.TextEditor._GetUndoManager();
        if (undoManager != null)
        {
            if (undoManager.IsEnabled)
                undoManager.Clear();
        }
    }
}
于 2013-09-01T21:40:46.230 回答
1

""创建对象而不String.Empty创建对象。所以使用 String.Empty 效率更高。

参考:String.Empty vs ""

关于.Clear()我没有得到更好的答案然后@syned 的答案。

于 2013-09-03T12:44:09.430 回答
1
txtUserName.Clear();

此代码清除文本框。它将文本框值设置为“”

txtUserName.Text = string.Empty;

不创建对象。txtUserName.Text = ""这比;执行得更快。

txtUserName.Text = "";

创建对象并影响性能。

于 2013-09-04T05:58:22.717 回答
1

让我们一一浏览命令。

txtUserName.Clear();

Clear()命令为 texbox 分配一个空字符串,就像下一个示例一样。 来源(syned 在这一点上给出了最好的解释)

txtUserName.Text = string.Empty;

现在它string.Empty的实际代码是

static String()
{
    Empty = "";
}

这意味着您在编译时间之后分配字符串“”。

txtUserName.Text = "";

现在在这里您只需在编译时将“”字符串直接分配给对象。

小边注txtUserName.Text = "";txtUserName.Text = string.Empty; Source快

于 2013-09-04T06:29:42.530 回答
1

string.Empty字段是一个空字符串文字。它与空字符串常量""略有不同。有一个微妙的区别——但在某些情况下可能很重要。它改变了程序的含义

我们在 C# 程序中使用string.Empty 和 "" 。string.Empty 字段在运行时由 .NET Framework 初始化为 ""。

你不能使用 string.Empty 作为 switch case,因为it cannot be determined at compile-time by the C# compiler.

这解释了 string.empty 和 "" 的区别

Clear()方法不仅仅是从TextBox. 它删除所有内容并重置文本选择

于 2013-09-06T12:01:41.990 回答
1

嗯.. 第一个警告,这个答案有可能超出大多数开发人员目前的共识,但这里是:) 尝试阅读它直到最后。

这两个(甚至我包括的另外一个)完全相同:

txtUserName.Text = "";
txtUserName.Text = string.Empty;
txtUserName.Text = null;

即使调试配置中的程序集可能会出现一些不同,我很肯定 在更优化的发布模式下它们将编译为完全相同的程序集。

如果它们的结果不同 - 这意味着编译器以最佳翻译方式翻译此案例的代码议程的能力下降,或者换句话说..在其他语言中,这可能来自同一个程序集并且来自它的学术愿景 - 它应该作为同一个程序集出现。但并不是每个编译器都关心那么多的学术观点:)

关于第三个家伙txtUserName.Clear(),这是一个不同的情况,我假设就像你一样,这个方法的内部实现实际上做或只是使用这三个任务中的一个..
(正如其他人已经提到的那样,它所做的不仅仅是从删除字符文本)
但是,如果您认为面向对象-假设有人想要创建一个特殊的文本框,其中包括例如要清除的更多内容-对他来说,使用“清除”方法来覆盖将非常方便..如果您使用了 clear 方法 - 当您从基本文本框更改为新的自定义/特殊 texbox 时,您不会更改代码。

所以总结一下 - 如果你要使用控件,你应该使用它的方法,这意味着当你想要清除它时使用'Clear()'方法将更合适,特别是如果你想要在未来的某一天用您自己的自定义文本框替换该文本框。所以至少在语法上它是更好的选择。
但是是的,如果你想要的只是从文本属性中删除字符,它会影响性能。

这里有一个小程序来测试WPF下各个的效率。

<Window x:Class="WpfApplication4.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <TextBox Name="txtbx1" Grid.Row="0"/>
    <TextBox Name="txtbx2" Grid.Row="1"/>
    <TextBox Name="txtbx3" Grid.Row="2"/>
    <TextBox Name="txtbx4" Grid.Row="3"/>
</Grid>

using System;
using System.Windows;

namespace WpfApplication4
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DateTime oldTime, newTime;
            TimeSpan delta;

            var iterations = 100000;

            #region Test performance 1

            oldTime = DateTime.Now;
            for (var i = 0; i < iterations; i++)
                txtbx1.Text = "";
            newTime = DateTime.Now;
            delta = newTime - oldTime;
            txtbx1.Text = delta.Milliseconds.ToString();

            #endregion

            #region Test performance 2

            oldTime = DateTime.Now;
            for (var i = 0; i < iterations; i++)
                txtbx2.Text = string.Empty;
            newTime = DateTime.Now;
            delta = newTime - oldTime;
            txtbx2.Text = delta.Milliseconds.ToString();

            #endregion

            #region Test performance 3

            oldTime = DateTime.Now;
            for (var i = 0; i < iterations; i++)
                txtbx3.Text = null;
            newTime = DateTime.Now;
            delta = newTime - oldTime;
            txtbx3.Text = delta.Milliseconds.ToString();

            #endregion

            #region Test performance 4

            oldTime = DateTime.Now;
            for (var i = 0; i < iterations; i++)
                txtbx4.Clear();
            newTime = DateTime.Now;
            delta = newTime - oldTime;
            txtbx4.Text = delta.Milliseconds.ToString();

            #endregion
        }
    }
}

这些是我得到的结果:43、40、73、443

它是一致的 - 前两个大约相同 +/- 微秒或两个,第三个总是略长,最后一个肯定比所有其他人都长。

我认为这已经很深了:)

于 2013-09-07T19:18:36.373 回答
0

有人说String.Empty""快,但是String.EMpty是初始化为""的静态成员

当我们调用String.EmptymakeIL调用

mscorlib.dll 

IL_0007:  ldsfld     string [mscorlib]System.String::Empty

而对于""它没有

IL_001a:  ldstr      ""

所以从逻辑上讲, “”String.Empty更有效会更有意义

于 2013-09-06T11:25:59.880 回答