I'm a total noob with c#, and cannot figure out why the same method works in different ways. I'm making a simple spreadsheet application, and am using a Dictionary of cells, where the key is a string name and the value is a Cell object:
public struct Cell
{
private string Name { get; }
public Object Content { get; set; }
public Cell(string n, Object o)
{
Name = n;
Content = o;
}
}
Now, I'll need to be able to easily add/change the contents of the cell, so I've been doing this:
Dictionary<string, Cell> cells = new Dictionary<string, Cell>();
// Assign new cell to 5.0 & print
cells.Add("a1", new Cell("a1", 5.0));
Console.WriteLine(cells["a1"].Content); // Writes 5
// Assign cell to new content & print
cells.TryGetValue("a1", out Cell value);
value.Content = 10.0;
Console.WriteLine(cells["a1"].Content); // Writes 5
Console.ReadKey();
Of course, the dictionary creates the new cell just fine, but when I use TryGetValue, the new content for the cell doesn't make it to the actual object I'm trying to get. I was expecting the second print to be 10. In debug, it seems like it instantiates a new Cell instead of getting the reference of the cell at hand.
I've used a Dictionary before, and I've used TryGetValue to change properties of the existing object. So here's two questions: What am I doing wrong in this case, and what factors determine if the method returns a reference or not?
Cell is a struct. It is not recommended that you use a struct for objects that can be modified. I think you just found out why.
When TryGetValue returns the struct, it copies it into a value, which is a different struct than the one in the Dictionary.
Imagine if you replaced struct by int - another value type - would you expect assigning to the int from TryGetValue to change the Dictionary entry int?
If other constraints require you use a struct, you will need to update the cells Dictionary with the new struct, just as you would with any other value type:
Dictionary<string, Cell> cells = new Dictionary<string, Cell>();
// Assign new cell to 5.0 & print
cells.Add("a1", new Cell("a1", 5.0));
Console.WriteLine(cells["a1"].Content); // Writes 5
// Assign cell to new content & print
cells.TryGetValue("a1", out Cell value);
value.Content = 10.0;
cells["a1"] = value; // update cells Dictionary
Console.WriteLine(cells["a1"].Content); // Writes 5
Console.ReadKey();
You need to make your struct Cell into a class Cell.
That's because struct is a value type and it's content can't be changed by reference.
If you want to go in detail, you can read about difference of value and reference types here.
Related
This question is basically the same as this one, although the answer to that person's problem turned out to be a simple trailing space.
My issue is that I'm retrieving data from a web API as dictionary and then trying get the values out of it. I'm using TryGetValue because not every item in the dictionary will necessarily contain every key. For some reason, whilst I can get the value of one key with no problems at all when it's present, for another key TryGetValue always evaluates to false and therefore doesn't return the value, even though I can see in debug that the key is present.
So, this block always retrieves the value of the "System.Description" key if it's present:
string descriptionValue = "";
if (workItem.Fields.TryGetValue("System.Description", out descriptionValue))
{
feature.Description = descriptionValue;
}
However, this almost identical block NEVER retrieves the value of the "CustomScrum.RoadmapGroup" key:
int RoadmapGroupValue = 0;
if (workItem.Fields.TryGetValue("CustomScrum.RoadmapGroup", out RoadmapGroupValue))
{
feature.RoadmapGroup = RoadmapGroupValue;
}
As you can see in this screenshot, the dictionary DOES contain a key with a name exactly matching my TryGetValue statement:
If I put a breakpoint on the code which should be run if the TryGetValue statement evaluates to true (feature.Description = descriptionValue;) it never gets hit.
The feature.RoadmapGroup variable gets set to 0 for every item in the dictionary.
I've been staring at this for the last two hours at least and I can't see what I'm doing wrong.
Here's a scenario where your cast goes wrong.
private void foo()
{
Dictionary<string, object> dict = new Dictionary<string, object>();
object obj = new object();
obj = "1";
dict.Add("CustomScrum.RoadmapGroup", obj);
object val;
var result = dict.TryGetValue("CustomScrum.RoadmapGroup", out val);
int value = (int)val;
}
TryGetValue() returns true, but the last line (the cast), throws System.InvalidCastException: 'Specified cast is not valid.', although if you use a breakpoint to see the dictionary content it looks like you have something that can be converted to an int. See below:
So I believe that when you add the value to the dictionary, you're not really adding an int but something that looks like an int.
EDIT
I just replaced int value = (int)val; with int value = Convert.ToInt32(val); which converts the value just fine. So you might want to try to use that and see if that works as well.
Are you sure that this "CustomScrum.RoadmapGroup" key is a string? If yes, then make sure that it doesn't contain any special unreadable character. You can just copy this value while debugging, put it in Watch window and check length/bytes representation, then do the same for hand-written string with the same content.
How can I find out whether a variable can be copied such that changing the original will not affect the copy (and vice versa)?
I thought checking its Type's IsValueType property would be enough, but noticed that strings are not considered value types, though a copied string behaves for this purpose, as a value type. Are there any others I have to check for?
EDIT
Yes. I realize a string is immutable. I just meant that off the top of my head it seemed like IsValueType is what I'm looking for. All I want is to be able to have copy = original and then never will a change to the variable original affect the variable copy. This is true for ints and strings and not for Forms for example.
EDIT 2
I'm looking for the likes of strings, ints, decimals, Points, enums, Sizes... As opposed to Forms, StringBuilders...
I don't see a way where you can determine this at runtime for any given type. The only way to accomplish this would be to create collection with all known types that have your desired behaviour. That woould be the built-in value type like int, double and so on + the reference type string.
As I mentioned in the comments you cannot just check for the type's IsValueType property because this would be true for struct's but struct's itself can contain objects. Consider the following code:
class Program
{
static void Main(string[] args)
{
var dataTable = new DataTable();
dataTable.Columns.Add("ABC", typeof(string));
var a = new CustomStruct() { IntValue = 1, DataTable = dataTable };
Console.WriteLine("IsValueType: {0}", a.GetType().IsValueType);
Console.WriteLine();
var b = a;
a.IntValue = 2;
a.DataTable.Columns.Add("DEF", typeof(int));
Console.WriteLine("a.IntValue: {0}", a.IntValue);
Console.WriteLine("a.DataTable.Columns.Count: {0}", a.DataTable.Columns.Count);
Console.WriteLine();
Console.WriteLine("b.IntValue: {0}", b.IntValue);
Console.WriteLine("b.DataTable.Columns.Count: {0}", b.DataTable.Columns.Count);
}
}
public struct CustomStruct
{
public int IntValue;
public DataTable DataTable;
}
The output is:
IsValueType: True
a.IntValue: 2
a.DataTable.Columns.Count: 2
b.IntValue: 1
b.DataTable.Columns.Count: 2
So you see, though CustomStruct is a value type b's DataTable object is the same as a's.
So creating a custom list with know types with the desired behaviour is the easiest way to go.
I have come across the following code in C# in Depth 2nd Edition by Jon Skeet and I don't understand how it works.
Dictionary<string,int> frequencies;
frequencies = new Dictionary<string,int>();
string[] words = Regex.Split(text, #"\W+");
foreach (string word in words)
{
if (frequencies.ContainsKey(word))
{
frequencies[word]++;
}
else
{
frequencies[word] = 1;
}
}
Specifically how does the "word" key get added to the dictionary? As I see it, a new dictionary is created called frequencies, it is empty. There is then a method to split a string called text into an array of string using Regex.Split. So far all good. Next there is a foreach loop which loops through the array, but the next part trips me up, it is checking if frequencies contains the particular word, if it does then increase the value of it by 1 or if it doesn't yet have a value set it to 1. But how does the dictionary get populated with the "word" key in the first place to allow it to be checked?
It looks to happen in this line
frequencies[word] = 1;
But I can't find a reference anywhere that says specifying a dictionary object followed by square brackets and an assignment to a value also populates the key. I thought you needed to use the add method of the dictionary instance or do so when initializing the dictionary.
If I am correct what is the name of this action?
frequencies[word] = 1;
is the same as calling
frequencies.Add(word, 1);
if the key word does not already exist. Otherwise you override the value.
When you call [something] on a dictionary you get a value by key something. The same goes for setting. When setting a value you can call dictionary[key] = value.
The function used is the [] operator (brackets operator).
I dove into the Object Browser and found this about the [] operator of the generic dictionary:
public TValue this[TKey key] { get; set; }
Member of System.Collections.Generic.Dictionary<TKey, TValue>
Summary: Gets or sets the value associated with the specified key.
Parameters: key: The key of the value to get or set.
Return Values: The value associated with the specified key. If the
specified key is not found, a get operation throws a
System.Collections.Generic.KeyNotFoundException, and a set operation
creates a new element with the specified key.
Exceptions: System.ArgumentNullException: key is null.
System.Collections.Generic.KeyNotFoundException: The property is
retrieved and key does not exist in the collection.
I'm trying to convert string collection type to dictionary that contain various types of keys and values(the value will be a struct most of the time.
each string in the string collection will contain the key in the first element and the rest of the string will contain the struct elements(The Values of the Dictionary), of course that the string will contain the right type of each element in the struct we just need to decode the struct elements' type...
i tried to do so but i ran into dead end and no possibility...
i tried to google it but my case is pretty hard because it suppose to match to a lot of cases in my program...
this is what i tried to do
private static void LoadDictionaryFromString<TKey,TValue>(ref IDictionary<TKey,TValue> argetDict,StringCollection SourceString)
{
TargetDict.Clear();
foreach(string strData in SourceString)
{
string[] strElements = strData.Split(';');
int m = 1;
// i must initialize
object Key = new object(); // when i try => TKey Key = new TKey() it's not working
object Value = new object(); // the same like previous line...
foreach (var field in typeof(TValue).GetFields())
{
// I can't work this out.... i stuck here
// in this line i'm getting an exception -> out of index...
TypeDescriptor.GetProperties(TargetDict.Values)[m].SetValue(Value , strElements[m]);
}
TypeDescriptor.GetProperties(TargetDict.Keys)[0].SetValue(Key, strElements[0]);
TargetDict.Add((TKey)Key,(TValue)Value);
}
}
I hope some one can help me with this because I struggled with this a lot.
Thanks
I'm adding a sample for the input of the string collection,
the string collection has been saved before from the same dictionary,
so let's say the current dictionary contains the key and value like this:
,
the key is the OrderID
the sample struct contain the fields:
struct sampleStruct
{
string StockName;
int Quantity;
int StockType;
enum_Status StockStatus; // the declaration: enum enum_Status { Accepted, Cancel, Executed }
double Price;
DateTime TradeTime;
}
now let's say we have two records that contain the values:
OrderID = 155 (for the key part) and for the struct : Apple , 200, 1, Accepted, 250, 12/05/2013 10:00:00
OrderID = 156 (for the key part) and for the struct : IBM, 105, 1, Executed, 200, 12/05/2013 12:34:10
so the saved string collection will be name
StringCollection strCollection;
"155;Apple;200;1;Accepted;250;12/05/2013 10:00:00
156;IBM;105;1;Executed;200;12/05/2013 12:34:10"
now, after a while i have a saved string collection from the kind above and i know that it contains the specified struct above so i run the function like this:
IDictionary<int, sampleStruct> DictToTarget=new IDictionary<int, sampleStruct>();
// the Dictionary will be cleared and will contain the data as saved in the string collection
// the stringCollection will contain the data above
LoadDictionaryFromString<int, sampleStruct>(ref DictToTarget, strCollection);
I hope that's enough data to help me.
Thanks again.
I have a list of Character objects I made and a list of Cells which can contain one character. Is it possible for my Character object to be added to the list and be assigned to a cell and changes I make to it in the party list or the cell to effect the object in both place? I don't really know how the pointers will work out for this. I figure what will happen is the object in the list is a separate object from the one assigned to the cell.
Sorry my code is very large so I don't want to post it all here but the Character I am talking about is a custom class I made. I suppose my question really boles down to two questions. When I put something in a list is that changing where the pointer points or is that a new object all together. Also can I have multiple pointer if I add something to a list then assign that to another instance of the character object will referencing the Character from the Cell Object be the Same as referencing my Character from the other list object.
As long as your Character is a class (i.e. a reference type) then you are essentially storing references to Character objects. Any changes you make to an object through a reference to it will be visible when the object is accessed through any other reference.
Example:
class Character
{
public string Name { get; set; }
}
var c = new Character();
var c2 = c;
var arr1 = new Character[] { c };
var arr2 = new Character[] { c };
arr1[0].Name = "Foo";
Console.WriteLine(arr2[0].Name); // "Foo"
Console.WriteLine(c2.Name); // also "Foo"
So basically you want to treat Characters as a reference type.
Just create your own wrapper object which contains a character inside it.
public MyCharacter
{
public char character{get;set;}
}
Objects are treated by reference, so that should work for you.