I'm trying to assign "tela[counter] = letra.ToString();", but it shows up the following error message "Property or indexer 'string.this[int] cannot be assigned to -- it is read only".
I saw some topics saying that you have to define {get;set;} to the atribute, but I've done this, and it didn't work.
[OBS] I tried to create get and set methods but the problem persisted
public String tela { get; set; }
private void btnSendWord_Click(object sender, EventArgs e)
{
char letra = Convert.ToChar(txtGetWord.Text);
MessageBox.Show(comboPalavra[0]);
Boolean codigoVerificador;
codigoVerificador = verificador.VerificaLetra(comboPalavra[0],letra);
if (codigoVerificador == true)
{
foreach(char c in comboPalavra[0].ToCharArray())
{
counter++;
if(c == letra)
{
tela[counter] = letra.ToString();
}
}
}
else MessageBox.Show("Nao contem");
}
Strings are immutable, meaning that you can't change a variable's value without creating a new string.
You are trying to change a single character in a string here, which is not allowed:
tela[counter] = letra.ToString();
A solution to this is to use StringBuilder. You can think of it as a mutable version of string.
You can declare a StringBuilder outside the loop:
var telaBuilder = new StringBuilder(tela);
In the loop, change the erroneous line to:
telaBuilder[counter] = letra;
And after the loop, assign telaBuilder to tela:
tela = telaBuilder.ToString();
Don't forget using System.Text!
This is the signature of the indexer for a string:
public char this[int index] { get; }
See that it returns a char and only has a get, that means it is readonly and you cannot assign something to it. This is why you are getting the error.
If you want to replace a specific letter in a string at a specific position, you can do it like this:
var name = "ferry Seinfeld";
char[] nameArray = name.ToArray();
nameArray[0] = 'J';
name = new string(nameArray);
Please look into Replace method of String and also look into StringBuilder class to see if those will suit your needs.
Related
Let's say I have the following code point: u1F64A (which is the 🙊 emoji).
This can be written as:
string monkey = "\u1F64A";
How would I convert a known codepoint (as an integer) to a string at runtime though?
int codepoint = 0xF64A;
string monkey = //?
When I want to play with Emojis in C#, I build a helper class just like this:
public class Emoji
{
readonly int[] codes;
public Emoji(int[] codes)
{
this.codes = codes;
}
public Emoji(int code)
{
codes = new int[] { code };
}
public override string ToString()
{
if (codes == null)
return string.Empty;
var sb = new StringBuilder(codes.Length);
foreach (var code in codes)
sb.Append(Char.ConvertFromUtf32(code));
return sb.ToString();
}
}
This way, I can just do string monkeyEmoji = new Emoji(0xF64A);
It also supports emojis with multiple code points (yes, those exist and are a pain)
Is the above code even compilable? It doesn't compile on my machine.
And why should it, \u1F64A is not a valid string.
I think what could work is string monkey = $"{char.ConvertToUtf32((char) 0xF64, 'A')}", but that is just a guess. I just answered to clarify that the first line of code you wrote is not compilable on my machine.
I've enabled the C# 8.0 non-nullable reference types feature in one of my projects, but now I'm unclear about how to represent missing data.
For example, I'm reading a file whose lines are colon-separated key/value pairs. Sometimes there's more than one colon on a line. In that case, the text before the first colon is the key, and the rest is the value. My code to parse each line looks like this:
public (string key, string value) GetKeyValue(string line)
{
var split = line.Split(':');
if (split.Length == 2)
return (split[0].Trim(), split[1].Trim());
else if (split.Length > 2)
{
var joined = string.Join(":", split.ToList().Skip(1));
return (split[0].Trim(), joined.Trim());
}
else
{
Debug.Print($"Couldn't parse this into key/value: {line}");
return (null, null);
}
}
What this does: If we have just one colon, return the key and value. If we have more than one, join the rest of the text after the first colon, then return the key and value. Otherwise we have no colons and can't parse it, so return a null tuple. (Let's assume this last case can reasonably happen; I can't just throw and call it a bad file.)
Obviously that last line gets a nullability warning unless I change the declaration to
public (string? key, string? value) GetKeyValue(string line)
Now in F# I would just use an Option type and in the no-colon case, I'd return None.
But C# doesn't have an Option type. I could return ("", ""), but to me that doesn't seem better than nulls.
In a case like this, what's a good way to say "I didn't find anything" without using nulls?
You could include if the result was successful in parsing by just returning a flag:
public class Result
{
private Result(){}
public bool Successful {get;private set;} = false;
public string Key {get; private set;} = string.Empty;
public string Value {get; private set;} = string.Empty;
public static Successful(string key, string value)
{
return new Result
{
Successful = true,
Key = key,
Value = value
};
}
public static Failed()
{
return new Result();
}
}
public Result GetKeyValue(string line){
return Result.Failed();
}
Then you could use it like
var result = GetKeyValue("yoda");
if(result.Successful)
{
// do something...
}
Alternatiely you could return 2 diffrent types and use pattern matching 👍
Actually, I realize now that part of the problem is that my method is doing two separate things:
Determine whether the line has a key.
Return the key and value.
Thus the return value has to indicate both whether there's a key and value, and what the key and value are.
I can simplify by doing the first item separately:
bool HasKey(string line)
{
var split = line.Split(':');
return split.Length >= 2;
}
Then in the method I posted, if there's no key, I can throw and say that the lines need to be filtered by HasKey first.
Putting on my functional thinking cap, an idiomatic return type would be IEnumerable<(string?,string?)>. The only change to your code would be to change return to yield return, and to remove the return statement if nothing is found.
public IEnumerable<(string? key, string? value)> GetKeyValue(string line)
{
var split = line.Split(':');
if (split.Length == 2)
return (split[0].Trim(), split[1].Trim());
else if (split.Length > 2)
{
var joined = string.Join(":", split.ToList().Skip(1));
yield return (split[0].Trim(), joined.Trim());
}
else
{
Debug.Print($"Couldn't parse this into key/value: {line}");
}
}
The caller then has several options on how to handle the response.
If they want to check if the key was found the old-fashioned eway, do this:
var result = GetKeyValue(line).SingleOrDefault();
if (!result.HasValue) HandleKeyNotFound();
If they prefer to throw an exception if the key is not found, they'd do this:
var result = GetKeyValue(line).Single();
If they just want to be quiet about it they can use ForEach, which will use the key and value if they are found and simply do nothing if they are not:
foreach (var result in GetKeyValue(line)) DoSomething(result.Item1, result.Item2);
Also, for what it's worth, I'd suggest using KeyValuePair instead of a tuple, since it clearly communicates the purpose of the fields.
I simply can't understand why this simple code is not working. My expected output is 10 and 15, but it is returning 2 and 3. That means that the update is not working.
List<int> numbers = new List<int>();
numbers.Add(2);
numbers.Add(3);
numbers.ForEach(n => n = n*5);
numbers.ForEach(n => Console.WriteLine(n));
Note: I've already searched a lot, but I could not understand this behavior.
How should I fix it?
Update: the same behavior for strings.
List<string> strings = new List<string>();
strings.Add("a");
strings.Add("b");
strings.ForEach(s => s = s + "--");
strings.ForEach(s => Console.WriteLine(s));
n is a copy of your current value in the list not a reference to your value.If you want to manipulate the values in your list then use a for loop
for(int i = 0; i<numbers.Count; i++)
numbers[i] *= 5;
More detailed explanation:
With a normal foreach loop your code doesn't even compile:
foreach(var n in numbers)
n = n * 5; // Readonly local variable cannot be used as an assignment target
Remember that List<T>.ForEach loop is not the same as foreach but it is just a method that takes a Action<int> delegate as argument and performs the specified action on the each element in your list.So it performs something like this (taken from the source code):
public void ForEach(Action<T> action)
{
// removed unnecessary parts for brevity
for(int i = 0 ; i < _size; i++)
{
action(_items[i]);
}
}
As you can see here the _item[i] is passed to the action and since int is a value types the copy of your value is passed rather than a reference.And that's why your values didn't change.
For strings: Apart from the fact that strings are immutable, assigning a new reference to a reference type doesn't change the object that holds the same reference.For example consider this:
static void Update(string s)
{
s = "bar";
}
string f = "foo";
Update(f);
Console.WriteLine(f); // foo
Assigning a new reference to s doesn't change the f, f stil holds the old reference and s is pointing to a new location in memory.This is not because s is a copy,it's not.If you change a property of s (with strings you can't do that but try with another reference type), it would update the property of f as well.It works in this way because s and f are two different strings that points to the same location in memory.So s is not bound to f.You can think they were declared like this:
string f = "foo";
string s = f;
s = "bar";
The only exception is when you pass f as a ref argument then the assignment will change the f as well:
static void Update(ref string s)
{
s = "bar";
}
string f = "foo";
Update(ref f);
Console.WriteLine(f); // bar
Because they are value types, rather than mutating the list you could create a modified one using Select
var newList= numbers.Select(n => n = n*5);
As imperative programmers, we love mutating things, which is not a brilliant idea!!
The reason why it did not work for strings is that because by default C# passes a copy of the reference rather than the actual reference.
void Fn(string s)
{
s = "not being changed";
}
Main()
{
var hello = "hello";
Fn(hello);
Console.WriteLine (hello); // prints hello again!!
}
However, if you want to change the reference you have to use the ref keyword.
void Fn(ref string s)
{
s = "Unfortunately, changed!";
}
Main()
{
var hello = "hello";
Fn(ref hello);
Console.WriteLine (hello); // Unfortunately, changed!!!
}
I think that changing parameters' values is a terrible idea and you shouldn't be doing that, you should return a new string that contains the new modifications.
The reason is because the parameter to the ForEach are passed by value and not by reference.
However, if you do pass a reference type, it must work as expected as shown below
class Program
{
static void Main(string[] args)
{
List<Frog> numbers = new List<Frog>();
numbers.Add(new Frog { name = "balcha" });
numbers.Add(new Frog { name = "Tibara" });
numbers.ForEach(n => n.name = "Bontu");
numbers.ForEach(n => Console.WriteLine(n.name));
Console.ReadLine();
}
class Frog
{
public string name { get; set; }
}
}
Output:
Bontu
Bontu
I am trying to convert an int var to a string var for use in a .txt file. i am coming up with a "unassigned local variable error". I have looked thru other questions but i don't see what i am missing. I have been able to convert int var to a string var before, i am not really sure where i am going wrong. If you could also give me the theory with the solution it would be most helpfull
int sbntmsk;
if (RBSBtn.Checked)
{
sbntmsk = 29;
}
if (BTSBtn.Checked)
{
sbntmsk = 30;
}
string subntmsk;
subntmsk = sbntmsk.ToString();
The compiler has no way to know if your checkboxes will be checked at runtime and so it complains because there is a possibility that the variable sbntmsk reaches the point where you try to convert it to a string without having a value assigned.
To fix the message declare and initialize sbntmsk with (or whatever default value you like)
int sbntmsk = 0;
You need to provide a default value for the integer. For example, what would you expect to be in the string if neither button was checked?
You could just use strings?
var sbntmsk = String.Empty;
if (RBSBtn.Checked)
{
sbntmsk = "29";
}
if (BTSBtn.Checked)
{
sbntmsk = "30";
}
Try using this approach:
int sbntmsk;
if (RBSBtn.Checked)
{
sbntmsk = 29;
}
else if (BTSBtn.Checked) // Notice the ELSE - IF
{
sbntmsk = 30;
}
else
{
sbntmsk = 0; // a default value
}
string subntmsk = String.Empty; // initialize with empty
subntmsk = sbntmsk.ToString();
Since using multiple checkboxes you are assigning to a same variable so no need to check all IF blocks. Also, using this way you have a possibility to define an 'ELSE' block at the end.
Hope it helps!
My goal is to check if a string contains any string values from a list, store that value and wrap it in HTML-tags. I'm kind of lost here.
My List:
public List<string> AccessModifiers
{
get { return _accessModifiers ?? (_accessModifiers = new List<string>()); }
}
With the values addes in a separate method:
AccessModifiers.Add("public");
AccessModifiers.Add("private");
AccessModifiers.Add("protected");
AccessModifiers.Add("internal");
AccessModifiers.Add("protected internal");
AccessModifiers.Add("void");
Let's say i was sendind the text protected void TagListView_ItemDataBound(object sender, ListViewItemEventArgs e) to the code below, what I want is to get the keywords protected and void as matchedItems to wrap in my HTML-tags. It feels like the Find method should check if the item is found, not the whole list, but I'm a bit lost here. Here's what I got so far:
foreach (var item in AccessModifiers)
{
if (UsedItems == null) // If the session is null...
{
UsedItems = new List<string>(); // ...a new one is initiated
}
if(!UsedItems.Contains(item)) // Check if the iterated item is found in the session-list
{
bool match = AccessModifiers.Any(ArticleTextBox.Text.Contains);
string matchedItem = AccessModifiers.Find(ArticleTextBox.Text.Contains);
if (match == true)
{
string openTag = "<span class='accessModifiers'>";
string closeTag = "</span>";
string newModifier = openTag + matchedItem + closeTag;
newText = newText.Replace(matchedItem, newModifier);
UsedItems.Add(matchedItem); // Add the matchedItem (used item) to the session-list not to be iterated again
}
}
}
return newText;
I can at this point only get the keyword protected to be stored, not void. It seems like it would be more simple than what I've come up with so far.
It seems like it would be more simple than what I've come up with so far.
Quite the reverse. In particular, while the example you've given is simple, the general case isn't:
public static void thisvoidmethodisstatic()
{
Console.WriteLine("private within text");
}
Basically you'll need code which understands the structure of code rather better. Simple search and replace isn't going to cut it.
The code highlighter I use on csharpindepth.com is based on a VB highlighter written by Jared Parsons. I'll see whether I can find the original source - but if not, I'm sure there are other open source code highlighters available.
EDIT: If you really are happy with just doing a naive search and replace, then you're definitely making it more complicated than you need to.
You're iterating over all the access modifiers and also looking for all of them in the loop
I would strongly recommend getting rid of the conditional list assignment - just always assign an empty list beforehand. Aside from anything else, it avoids you having to check for nullity elsewhere.
Then you've just got:
foreach (var modifier in AccessModifiers)
{
if (ArticleTextBox.Text.Contains(modifier))
{
UsedItems.Add(modifier);
string openTag = "<span class='accessModifiers'>";
string closeTag = "</span>";
string newModifier = openTag + matchedItem + closeTag;
newText = newText.Replace(matchedItem, newModifier);
}
}