Reputation: 21
I have the following code which is intended, as an example, to take a fruitName and assign it to the next available unused "name#" string variable (one that does not already have a fruit name assigned to it).
Wanting to avoid using nested IF statements, i am trying to use a Dictionary as follows.
public static void AssignFruitToNextAvailableSlot(string fruitName)
{
string NextEmptyNameSlot = "";
string Name1 = "Apple";
string Name2 = "Orange";
string Name3 = "Tomato";
string Name4 = "";
string Name5 = "";
string Name6 = "";
string Name7 = "";
string Name8 = "";
string Name9 = "";
Dictionary<string, string> nameSlots = new Dictionary<string, string>()
{
{"Slot1", Name1},
{"Slot2", Name2},
{"Slot3", Name3},
{"Slot4", Name4},
{"Slot5", Name5},
{"Slot6", Name6},
{"Slot7", Name7},
{"Slot8", Name8},
{"Slot9", Name9}
};
foreach (KeyValuePair<string, string> nameSlot in nameSlots)
{
NextEmptyNameSlot = nameSlot.Key;
if (nameSlot.Value == "")
{
break;
}
}
Console.WriteLine($"Available name slot at {NextEmptyNameSlot}");
Console.WriteLine($"Content of empty name slot \"{nameSlots[NextEmptyNameSlot]}\"");
nameSlots[NextEmptyNameSlot] = fruitName;
Console.WriteLine($"Empty image slot has been assigned the value {nameSlots[NextEmptyNameSlot]}");
Console.WriteLine($"Empty image slot has been assigned the value {Name4}");
Console.ReadLine();
}
Sample output for AssignFruitToNextAvailableSlot("Strawberry") :
As you can see the code works fine to identify the empty name slot, in this case Slot4. However when using the syntax...
nameSlots[NextEmptyNameSlot] = fruitName
... "strawberry" is assigned to nameSlots[NextEmptyNameSlot], but not the variable Name4. I tried using "ref" to assign by reference but that yielded various error messages.
** What is the right syntax to assign the fruitName "Strawberry" to the Name4 string variable using the dictionary? Sorry if this is a very basic question. I am new to C#. **
Upvotes: 0
Views: 851
Reputation: 74740
While it's not the primary problem here, a confusing factor is that strings are immutable, which means if you "change it" then it's thrown away and a new one is created. I'll come back to this point in a moment
The major problem here is that you can not establish a reference to something:
string s = "Hello";
Then establish another reference to it (like you're doing with dictionary)
string t = s;
Then "change the original thing" by changing this new reference out for something else:
t = "Good bye";
And hope that the original s
has changed. s
still points to a string saying "Hello". t
used to point to "Hello" also (it never pointed to s pointed to hello in some sort of chain) but now points to a new string "Good bye". We never used the new
keyword when we said "Good bye" but the compiler had to use it, and new
made another object and changed the reference to it. Because references aren't chained, you cannot change what a downstream variable points to and hope that an upstream variable pointing to the same thing will also change.
//we had this
s ===> "Hello" <=== t
//not this
t ==> s ==> "Hello"
//then we had this
s ===> "Hello" t ===> "Good bye"
Because we have two independent reference that point to the same thing, the only way you can operate on one reference and have the other see it is by modifying the contents of what they point to, which means you will have to use something mutable, and not throw it away. This is where string confuses things because strings cannot be modified once made. They MUST be thrown away and a new one made. You don't see the new
- the compiler does it for you.
So instead of using strings, we have to use something like a string but can have its contents altered:
StringBuilder sb1 = new StringBuilder("Hello");
StringBuilder sb2 = null;
var d = new Dictionary<string, StringBuilder>();
d["sb1"] = sb1;
d["sb2"] = sb2;
Now you can change your string in the mutable sb1
, by accessing the stringbuilder via the dictionary:
d["sb1"].Clear();
d["sb1"].Append("Good bye");
Console.Write(sb1.Length); //prints 8, because it contains: Good bye
But you still cannot assign new references to your sb variables using the dictionary:
d["sb2"] = new StringBuilder("Oh");
//sb2 is still and will always be null
Console.Write(sb2.Length); //null reference exception
Using new
here stops the dictionary pointing to sb2
and points it to something else, sb2
is not changed and is still null. There is no practical way to set sb2
to a stringbuilder instance, by using the dictionary d
This is also why the original string thing didn't work out - you can't change the content of a string - c# will throw the old string away and make a new
one and every time new
is used any reference that might have pointed to the old thing will now point to a new thing
As a result you'll have to init all your references to something filled or empty:
var sb1 = new StringBuilder("Hello");
var sb2 = new StringBuilder("Goodbye");
var sb3 = new StringBuilder("");
var sb4 = new StringBuilder("");
You'll have to link your dictionary to all of them:
d["sb1"] = sb1;
d["sb2"] = sb2;
d["sb3"] = sb3;
d["sb4"] = sb4;
And you'll have to skip through your dictionary looking for an empty one:
//find empty one
for(int i = 1, i <= 4; i++){
if(d["sb"+i].Length ==0)
return "sb"+i;
}
And then change its contents
This is all maaassively complex and I wholeheartedly agree with the answer given by jason that tells you to use arrays (because it's what they were invented for), but I hope i've answered your questions as to why C# didn't work the way you expected
Upvotes: 1
Reputation: 1031
If such strange thing is absolutely necessary, you can use Reflection.
var frutSlots = this.GetProperties()
.Where(p => p.Name.StartsWith("Name")).OrderBy(p => p.Name).ToList();
You'll get List of PropertyInfo
objects, ordered by property name, through which you can iterate or just use Linq.
fruitSolts.First(fs => fs.GetValue(this).ToString()="").SetValue(this."somefruit");
But mind that reflections are not too quick and not too good for performance.
Upvotes: -1
Reputation: 9650
I think you are making this more complex than it needs to be. If you have a fixed number of elements and you want the order to remain the same then an array is the simplest way to handle the data.
Declare the array with 9 elements.
string[] fruitArray = new string[9];
Then keep a counter of how many slots you have used;
int NextSlot = 0;
Increment it after you add a fruit
NextSlot++;
Then you can iterate it to display the data
for(int loop =0; loop <= fruitArray.Length; loop++)
{
Console.WriteLine($"Slot {loop + 1} contains the value {fruitArray[loop]}");
}
If you don't have a fixed number of elements, or you don't know the size during design time then you can use a List<string>
instead of an array.
A List will keep the insertion order of the data, until you sort it.
Upvotes: 1