TextBox.Text += "string"; vs TextBox.AppendText("string"); - c#

what is the difference between these two methods?
Is one more efficient than the other?
I was thinking maybe the AppendText() uses a method similar to the StringBuilder, ie it uses its own cache instead of creating and appending a new string each time, is that true?
Thanks.

As it is clearly mentioned in Remarks section of MSDN Documentation
The AppendText method enables the user to append text to the contents of a text control without using text concatenation, which, can yield better performance when many concatenations are required.
Your question,
what is the difference between these two methods?
We all know how TextBox.Text += something; will work i.e. creating and appending a new string each time but how AppendText works I could not find any code snippet whether internally it uses StringBuilder or something else.
Is one more efficient than the other?
I think answer to above question will depend on the situation, (Based on Test case observation)
if Multiline property is set to false then Concatenation (+=) yields better results but on other hand Multiline property is set to true then AppendText yields far better performance.
EDIT After reading the comment from Rawling I made a custom win-form solution in which I had a simple textbox in which I appended a simple string hello 10000 times using a simple for-loop
private void btnAppendText_Click(object sender, EventArgs e)
{
txtText.Text = string.Empty;
DateTime startTime = DateTime.Now;
for (int i = 0; i < 10000; i++)
{
txtText.AppendText(s);
}
DateTime endTime = DateTime.Now;
txtTime.Text = (endTime.Ticks - startTime.Ticks).ToString();
}
private void btnConcante_Click(object sender, EventArgs e)
{
txtText.Text = string.Empty;
DateTime startTime = DateTime.Now;
for (int i = 0; i < 5000; i++)
{
txtText.Text += s;
}
DateTime endTime = DateTime.Now;
txtTime.Text = (endTime.Ticks - startTime.Ticks).ToString();
}
Output were very surprising,
TEST 1: Multiline property is true
I had to reduce the iteration to half i.e. 5000 for text concatenation as it was taking a very long time.
btnAppendText_Click output on txtTime was 37222129 almost 3-4 seconds for 10000 iteration
btnConcante_Click output on txtTime was 14449906487 more than 25 minutes for only 5000 iterations.
From the above result it is really clear that, AppendText is much faster and more efficient (when Multiline is true) than Concatenation
TEST 2: Multiline property is false
btnConcante_Click output on txtTime was 39862280 almost 3-4 seconds for 10000 iteration
btnAppendText_Click output on txtTime was 1043279672 almost 2-3 minutes for 10000 iteration
From the above result it is really clear that, Concatenation is faster and more efficient (when Multiline is false) than AppendText

The AppendText has nothing to do with StringBuilder. The Text method actually seems simpler (an possibly more performant). See source code of those two methods for reference:
public void AppendText(string text)
{
if (text.Length > 0)
{
int start;
int length;
this.GetSelectionStartAndLength(out start, out length);
try
{
int endPosition = this.GetEndPosition();
this.SelectInternal(endPosition, endPosition, endPosition);
this.SelectedText = text;
}
finally
{
if (base.Width == 0 || base.Height == 0)
{
this.Select(start, length);
}
}
}
}
public override string Text {
get {
return base.Text;
}
set {
if (value != base.Text) {
base.Text = value;
if (base.IsHandleCreated) {
base.SendMessage(185, 0, 0);
}
}
}
}

As a complement to dbw (and in case someone can find where I've made a mistake), here's my performance test:
private void Form1_Click(object sender, EventArgs e)
{
Stopwatch sw = new Stopwatch();
sw.Reset();
textBox1.Text = "";
sw.Start();
for (int i = 0; i < 10000; i++)
{
textBox1.Text += s;
}
sw.Stop();
var e1 = sw.Elapsed;
sw.Reset();
textBox1.Text = "";
sw.Start();
for (int i = 0; i < 10000; i++)
{
textBox1.AppendText(s);
}
sw.Stop();
var e2 = sw.Elapsed;
}
I see e1 with about 3 seconds and e2 with about 2 minutes.

Related

How to display array of strings on a label in string by string in a loop?

This code is now in the form1 constructor.
if (filesRadar.Length > 0)
{
for (int i = 0; i < filesRadar.Length; i++)
{
label2.Text = dates[i].ToString("ddd, dd MMM yyy HH':'mm");
}
}
I want to display the strings in dates when calling a method or something in the constructor and also in other places like when downloading finished. each time calling a method or enabling timer to display the strings in a loop in specific speed.
Now it's displaying but too fast then in the end displaying the last item only and it's not in loop.
or enabling timer to display the strings in a loop in specific speed.
What's keeping you from doing that?...
Drop the timer on your form and set its Interval property to something reasonable. For instance, an interval of 1000 would mean it changes every second. Make sure the Timer is turned on (Enabled).
Now move the i variable out to class level and get rid of the for loop:
private int i = -1;
private void timer1_Tick(object sender, EventArgs e)
{
if (filesRadar.Length > 0)
{
i++;
if (i >= filesRadar.Length)
{
i = 0;
}
label2.Text = dates[i].ToString("ddd, dd MMM yyy HH':'mm");
}
else
{
label2.Text = ""; // optional?
}
}
You do not need the if-statement, because the loop will only execute if there are items in the array:
for (int i = 0; i < filesRadar.Length; i++)
{
label2.Text += dates[i].ToString("ddd, dd MMM yyy HH':'mm") + Enviroment.NewLine;
}
You can make this as a void method and you can call whenever you want to:
public void ReportTime()
{
for (int i = 0; i < filesRadar.Length; i++)
label2.Text += dates[i].ToString("ddd, dd MMM yyy HH':'mm") + Environment.NewLine;
}

Word Fast Counting

I'm implementing a word count feature for my ASP.NET server, and I was wondering what would be the fastest method of doing so, as I'm not sure using a simple
text.AsParallel().Count(Char.IsWhiteSpace);
is the fastest possible method. Since this feature might be used quite a bit on relatively long walls of text, I want it to be as fast as possible, even if it means using unsafe methods.
Edit: Some benchmarking with Rufus L's code as well as my own unsafe method:
public static unsafe int CountWords(string s)
{
int count = 0;
fixed (char* ps = s)
{
int len = s.Length;
char* pc = ps;
while (len-- > 0)
{
if (char.IsWhiteSpace(*pc++))
{
count++;
}
}
}
return count;
}
Split(null): 681979 words in 415867 ticks.
Count(WhiteSpace): 681978 words in 147860 ticks.
AsParallel: 681978 words in 401077 ticks.
Unsafe: 681978 words in 98139 ticks.
I'm still open to any better ideas :)
EDIT2:
Rewrote the function, taking care of multiple white spaces too:
public static unsafe int CountWords(string s)
{
int count = 0;
fixed (char* ps = s)
{
int len = s.Length;
bool inWord = false;
char* pc = ps;
while (len-- > 0)
{
if (char.IsWhiteSpace(*pc++))
{
if (!inWord)
{
inWord = true;
}
}
else
{
if (inWord)
{
inWord = false;
count++;
}
}
if (len == 0)
{
if (inWord)
{
count++;
}
}
}
}
return count;
}
Split(null): 681979 words in 517055 ticks.
Count(WhiteSpace): 681978 words in 148952 ticks.
AsParallel: 681978 words in 410289 ticks.
Unsafe: 660000 words in 114833 ticks.
According to my tests, this is much faster, by a factor of 4 (but see the update below for different results):
wordCount = text.Split(null).Length;
Here's the test, in case you want to try it out. Note that adding AsParallel() slows the process down on my machine, due to the cost of task switching:
public static void Main()
{
var text = File.ReadAllText("d:\\public\\temp\\temp.txt");
int wordCount;
var sw = new Stopwatch();
sw.Start();
wordCount = text.Split(null).Length;
sw.Stop();
Console.WriteLine("Split(null): {0} words in {1} ticks.", wordCount,
sw.ElapsedTicks);
sw.Restart();
wordCount = text.Count(Char.IsWhiteSpace);
sw.Stop();
Console.WriteLine("Count(WhiteSpace): {0} words in {1} ticks.", wordCount,
sw.ElapsedTicks);
sw.Restart();
wordCount = text.AsParallel().Count(Char.IsWhiteSpace);
sw.Stop();
Console.WriteLine("AsParallel: {0} words in {1} ticks.", wordCount,
sw.ElapsedTicks);
}
Output:
Split(null): 964 words in 629 ticks.
Count(WhiteSpace): 963 words in 2377 ticks.
AsParallel: 963 words in 208983 ticks.
Update
After making the string MUCH longer (OP mentioned 100's of 1000's of words), the results became much more similar, and the Count(WhiteSpace) method became faster than the Split(null) method:
Code change:
var text = File.ReadAllText("d:\\public\\temp\\temp.txt");
var textToSearch = new StringBuilder();
for (int i = 0; i < 500; i++) textToSearch.Append(text);
text = textToSearch.ToString();
Output:
Split(null): 481501 words in 185135 ticks.
Count(WhiteSpace): 481500 words in 101373 ticks.
AsParallel: 481500 words in 336117 ticks.
After some benchmarking, the following unsage code yielded the fastest result in any case with 500+ words:
public static unsafe int CountWords(string s)
{
int count = 0;
fixed (char* ps = s)
{
int len = s.Length;
bool inWord = false;
char* pc = ps;
while (len-- > 0)
{
if (char.IsWhiteSpace(*pc++))
{
if (!inWord)
{
inWord = true;
}
}
else
{
if (inWord)
{
inWord = false;
count++;
}
}
if (len == 0)
{
if (inWord)
{
count++;
}
}
}
}
return count;
}
Answering your ask, the méthod AsParallel() is very fast. But exists more options, by e.g.:
Using Regex:
string input = "text text text text";
string pattern = "(-)";
string[] substrings = Regex.Split(input, pattern); // Split on hyphens
Console.WriteLine("Words: {0}", substrings.count());
But I reiterate, the AsParallel () method is very fast. You can do a proof of concept, to find out which is better. Place a stopwatch () at the beginning and another at the end of code and compare the AsParallel runtime () with the regex, so will have a more 'exact' answer.
Update
Using Parallel.For:
static void Main(string[] args)
{
string text = #"text text text text text text text text text text ";
int count = 0;
Console.WriteLine("Generating words, wait...");
Parallel.For(0, 100000, i =>
{
text += #"text text text text text text text text text text ";
});
var sw = new Stopwatch();
sw.Start();
Parallel.For(0, text.Length, i =>
{
if (char.IsWhiteSpace(text[i]))
count++;
});
sw.Stop();
Console.WriteLine("Words: {0} in {1} ticks", count, sw.ElapsedTicks);
Console.ReadLine();
}
Results:
PS: Note that the Parallel.For used is not managed

How can I get the the waiting times for access or failing in a function that is locked by threads?

I'm using a function to add some values in an Dynamic Array (I know that I could use a list but it's a requirement that I must use an Array).
Right now everything is working but I need to know when a thread fails adding a value (because it's locked and to save that time) and when it adds it (I think when it adds, I already have it as you can see in the function Add.
Insert Data:
private void button6_Click(object sender, EventArgs e)
{
showMessage(numericUpDown5.Value.ToString());
showMessage(numericUpDown6.Value.ToString());
for (int i = 0; i < int.Parse(numericUpDown6.Value.ToString()); i++)
{
ThreadStart start = new ThreadStart(insertDataSecure);
new Thread(start).Start();
}
}
private void insertDataSecure()
{
for (int i = 0; i < int.Parse(numericUpDown5.Value.ToString()); i++)
sArray.addSecure(i);
MessageBox.Show(String.Format("Finished data inserted, you can check the result in: {0}", Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
"times.txt")), "Result", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
Function to Add:
private object padLock = new object();
public void addSecure(int value)
{
Stopwatch sw = Stopwatch.StartNew();
string values = "";
lock (padLock)
{
try
{
if (array == null)
{
this.size = 1;
Resize(this.size);
array[0] = value;
count++;
}
else
{
count++;
if (size == count)
{
size *= 2;
Resize(size);
}
array[count - 1] = value;
}
}
catch
{
throw new System.ArgumentException("It was impossible to insert, try again later.", "insert");
}
values=String.Format("Element {0}, Time taken: {1}ms", value.ToString(), sw.Elapsed.TotalMilliseconds);
sw.Stop();
saveFile(values);
}
Sorry for asking this question but I have read different articles and this is the last one that I tried to use: http://msdn.microsoft.com/en-us/library/4tssbxcw.aspx but when I tried to implement in my code finally crashed in an strange error.
I'm afraid I might not completely understand the question. It sounds like you want to know how long it takes between the time the thread starts and when it actually acquires the lock. But in that case, the thread does not actually fail to add a value; it is simply delayed some period of time.
On the other hand, you do have an exception handler, so presumably there's some scenario you expect where the Resize() method can throw an exception (but you should catch only those exceptions you expect and know you can handle…a bare catch clause is not a good idea, though the harm is mitigated somewhat by the fact that you do throw some exception the exception handler). So I can't help but wonder if that is the failure you're talking about.
That said, assuming the former interpretation is correct – that you want to time how long it takes to acquire the lock – then the following change to your code should do that:
public void addSecure(int value)
{
Stopwatch sw = Stopwatch.StartNew();
string values = "";
lock (padLock)
{
// Save the current timer value here
TimeSpan elapsedToAcquireLock = sw.Elapsed;
try
{
if (array == null)
{
this.size = 1;
Resize(this.size);
array[0] = value;
count++;
}
else
{
count++;
if (size == count)
{
size *= 2;
Resize(size);
}
array[count - 1] = value;
}
}
catch
{
throw new System.ArgumentException("It was impossible to insert, try again later.", "insert");
}
sw.Stop();
values = string.Format(
"Element {0}, Time taken: for lock acquire: {1}ms, for append operation: {2}ms",
value.ToString(),
elapsedToAcquireLock.TotalMilliseconds,
sw.Elapsed.TotalMilliseconds - elapsedToAcquireLock.TotalMilliseconds);
saveFile(values);
}
}
That will display the individual times for the sections of code: acquiring the lock, and then actually adding the value to the array (i.e. the latter not including the time taken to acquire the lock).
If that's not actually what you are trying to do, please edit your question so that it is more clear.

Create a textbox with "smart" password char

I'm using .NET 3.5 CF and trying to create a textbox which should hide the character written inside it. This works by setting the parameter PasswordChar to = "*". However this change the char to a * directly.
What I want is a "smart" textbox who change the character to a * after a delay (approximate 1 second) which will make the user to get some feedback that correct character was written.
I tried this by creating another thread that should handle this since the user should still be able to write more characters and not wait this delay. I did something like this:
if (UseModernPasswordScreenMaskning)
{
this.Invoke(new UpdateTextCallback(this.UpdateText), new object[]{"*"});
}
private void UpdateText(string text)
{
int k = this.Text.Length;
System.Threading.Thread.Sleep(1000);
this.Text = this.Text.Remove(k - 1, 1);
this.Text = this.Text.Insert(k - 1, "*");
}
It works but the sleep is on my current thread which make next letter written delay by 1 second before it shows up. I want each letter to show directly and change to a *, 1 second after that particular char was written.
As i understand, a "smart" password textbox is
the last character will become * within 1s
if user type a new character, the older one will become * (although it doesn't last 1s)
So
Create a timer callback with 1s interval to change last character to *
In smartTextBox_TextChanged event, change the previous character to *
Here is my code
public void Do(object state)
{
int num = this.textBox1.Text.Count();
if (num > 0)
{
StringBuilder s = new StringBuilder(this.textBox1.Text);
s[num - 1] = '*';
this.Invoke(new Action(() =>
{
this.textBox1.Text = s.ToString();
this.textBox1.SelectionStart = this.textBox1.Text.Count();
timer.Dispose();
timer = null;
}));
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (timer == null)
{
timer = new System.Threading.Timer(timerCallback, null, 1000, 1000);
}
int num = this.textBox1.Text.Count();
if (num > 1)
{
StringBuilder s = new StringBuilder(this.textBox1.Text);
s[num - 2] = '*';
this.textBox1.Text = s.ToString();
this.textBox1.SelectionStart = num;
}
}
In initialize
timerCallback = new TimerCallback(Do);
I managed to solve it by adding since the line "int num = this.textBox1.Text.Count();" could not be used before that statement:
public void Do(object state)
{
if (this.logonPwTxtBox.InvokeRequired)
{ .. }
}

Threaded Code Is Causing the UI To Hang

I wrote a simple class to generate arrays as I needed some hard-coded random arrays for my own debugging purposes, however despite calling the timely operations in a separate worker thread my UI still hangs! Here's the code...
private static Random randGenerator = new Random();
private void generateButton_Click(object sender, EventArgs e)
{
string dataType = "System.";
if (typeComboBox.Text != "Byte")
dataType += signedCheckBox.Checked ? "" : "U";
else if (typeComboBox.Text == "Byte")
dataType += signedCheckBox.Checked ? "S" : "";
dataType += typeComboBox.Text;
generateButton.Enabled = false;
new Thread(() =>
{
Process(Type.GetType(dataType), (int)sizeNumericUpDown.Value, hexCheckBox.Checked);
}).Start();
}
private void Process(Type type, int size, bool hex)
{
StringBuilder sBuilder = new StringBuilder();
sBuilder.Append(string.Format(#"{0}[] values = new {0}[{1}] {{", type.Name, size));
for (int i = 0; i < size; i++)
{
int random = randGenerator.Next(0, GetIntegralMaxValue(type));
sBuilder.Append((hex ? "0x" + random.ToString("x2") : random.ToString()) + (i < size - 1 ? ", " : " };"));
}
outputTextBox.BeginInvoke((MethodInvoker)delegate
{
outputTextBox.Text = sBuilder.ToString();
});
}
private int GetIntegralMaxValue(Type type)
{
var field = type.GetField("MaxValue").GetValue(null);
return Convert.ToInt32(field);
}
I thought that maybe the issue lied with trying to access objects created on the main thread so instead I passed them to the method. I also tried declaring my randGenerator object within the class but still no luck. Could anybody identify the issue?
The TextBox is not designed to hold a large set of data - it becomes very slow when amount of data increases. I would say that populating of the StringBuilder takes about 5% of time while assigning this data to TextBox (which executes in UI thread) takes remaining 95% of time. You can easily check this. Just run this code in debug mode and click "Pause" button during hanging. It should break on "outputTextBox.Text = sBuilder.ToString();" line.

Categories