This question already has answers here:
Error: "Cannot modify the return value" c#
(8 answers)
Closed 8 years ago.
I have a SortedDictionary defined as:
public SortedDictionary<DateTime,RosterLine> RosterLines = new SortedDictionary<DateTime,RosterLine>();
RosterLine itself is a simple struct:
struct RosterLine {
public string RosCd;
public string ActCd;
public double Hrs;
}
I can .Add(dt, rosterLine) no problems, and iterate through the dictionary fine too.
My problem is trying to update the RosterLine values given a specified date eg.
DateTime currDt = new DateTime(2013,12,02);
RosterLines[currDt].ActCd = "SO"; // error here
It tells me: Cannot modify the return value (dictionary def here) because it is not a variable. My goal is to do this with an iterating loop (which I thought might be the problem), but it won't work outside the loop on its own either (as above).
My question is: how do I update a SortedDictionary with a given key (date)?
The reason for the error message is that RosterLine is a struct and by that a value type. The error I get in ideone is:
Cannot modify a value type return value of
`System.Collections.Generic.SortedDictionary.this[System.DateTime]'.
Consider storing the value in a temporary variable
For value types, the dictionary stores a copy of the value and not a reference to the object on the heap. Also, when retrieving the value (as in dict[DateTime.Today]), it is copied again. Therefore, changing a property in the way you do in your sample only works on the copy of the value type. The compiler prevents misunderstandings by the error message - if it wouldn't one would wonder why the value in the dict has not been changed.
var dict = new SortedDictionary<DateTime, RosterLine>();
dict.Add(DateTime.Today, new RosterLine());
// Does not work as RosterLine is a value type
dict[DateTime.Today].ActCd = "SO";
// Works, but means a lot of copying
var temp = dict[DateTime.Today];
temp.ActCd = "SO";
dict[DateTime.Today] = temp;
In order to solve this, you could make RosterLine a class or you can work with temp variables as the error message suggests.
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.
This question already has answers here:
Func<T> with out parameter
(4 answers)
Closed 7 years ago.
Ignoring the irrelevant parts - I have a requirement to use a delegate with no arguments and no return value, but get a value from it.
The current solution is to use a lambda expression and a variable that's declared before it.
string result;
RequiredMethod(() => { result = "the result"; });// Gets the result from a 2nd thread.
//use result
Is there a way to do this without using a lambda expression? I expect there should be, but can't come up with it.
Yes of course there is:
public class ValueHolder
{
public string Value { get; private set; }
public void AssignValue()
{
this.Value = "the result";
}
}
// usage
var vh = new ValueHolder();
RequiredMethod(vh.AssignValue);
// access value
vh.Value
The code you provided told me that you try to access values from another thread. Please keep in mind that you should not access vh.Value until AssignValue has been called. You need to add some other code to sync these operations (but your lambda has exactly the same problem).
also the question is why do you have to use a delegate. If it's about getting the value you could even implement it in a way like this
string someString;
RequiredMethod(ref someString);
Of course this would change the signature of the RequiredMethod (and the question becomes why you couldn't return a value in the first place.).
You could even write an overload of the 2nd using your lamdba expression.
void RequireMethod(string ref variable) {
RequireMethod(() => { variable = "the result";});
}
This question already has answers here:
C# compiler error: "not all code paths return a value"
(9 answers)
Closed 7 years ago.
I have this program that i am writing that calculates GPAs and quality points for each course after the student enters their letter grade and credit hours.
The part i am having an issue with is the Calculation of the quality points for each course. I have the letter grades and credit hours dumped into 2 different arrays and I created a third array called QualityPts, which will be used to store the total quality points for each class.
This will be calculated by using the index position to determine the two values that will be used in the other 2 arrays. Now i am doing this a another method, and i am getting an error saying
"Not all code paths return a value".
The second error relates to my new variable "QualityPts" that is only in this new method. It says "Use of unassigned local variable".
These 2 errors are both in the method CalcQP().
My code is as follows:
private decimal[] grades;
private decimal[] Credits;
private decimal CalcQP()
{
decimal[] QualityPts;
string msg="The total quality Points for this course is: ";
for (int i = 0; i < grades.Length; i++)
{
QualityPts[i] = grades[i] * Credits[i];
lbQuality.Items.Add(msg + QualityPts[i]);
}
}
Looks like CalcQP() return type should be void. Is the purpose of the method just to add items to lbQuality? I'm not sure what else it should be returning from the code snippet.
Essentially the compiler is stopping because it can see that the method should be returning a double but contains no return statement.
The second error is saving you a runtime error because the compiler can see you're trying to use a variable which hasn't been initialised. So you just need to initialise QualityPts:
decimal[] QualityPts = new decimal[grades.Length];
private decimal calcQP() method should return a decimal value or change decimal to void.
Also, QualityPts should be initialized: decimal[] QualityPts = new decimal[grades.Length];
you are declaring QualityPts but not initializing it. That's why its giving error. Follow standard practice which says always initialize variable when you declare it. Do it like below:
decimal QualityPts = new decimal[length]; // Replace length with your length.
Second thing CalcQP() methods return type is decimal while you are not returning anything. You must have to return decimal value from it. Either make that method void. Do it like below:
public void CalcQP()
Let me know if you have another problem.
Not all code paths return a value
You have declared your method to need a return type of decimal
private decimal CalcQP()
but you never return a decimal.
You need to either return one (return QualtityPts[i])
Return the array
private decimal[] CalcQP()
... return QualityPts;
or just don't return anything
private void CalcQP()
Use of unassigned local variable
You are trying to use QualityPts but you haven't declared it so [i] won't have any value (or exist yet)
decimal[] QualityPts = new decimal[grades.Length];
This question already has answers here:
Directly modifying List<T> elements
(6 answers)
Closed 8 years ago.
I am writing a function which is passed in a list which is partially filled. I'd like to set some of the fields within the list inside this function. I thought that passing it as a reference would allow me to do this, however, I get the following error:
Error 1 Cannot modify the return value of 'System.Collections.Generic.List.this[int]' because it is not a variable
I am wondering what I might need to do to tell C# that I wish to have the option of modifying the contents of the list.
Here is a summarized version of my code:
public static void Determine_RTMM_Descriptor(ref List<Struct_Descriptor_Type> symbols, string Dwarf_Output_Filename)
{
...
lines = System.IO.File.ReadAllLines(Dwarf_Output_Filename);
//loop on symbol names
for (int idx = 0; idx < symbols.Count; idx++)
{
if(symbols[idx].size == 0)
symbols[idx].size = (int)new System.ComponentModel.Int32Converter().ConvertFromString(split_line[DwarfInterface.SIZE_INDEX]);
...
}
Thanks in advance for any help.
The underlying issue here is that you have a list of value types. When you use the indexer of the list to get an item from the list you are getting a copy of that type. The code symbols[idx] is the value of that item. It is not a variable representing that item, as the error message is telling you.
You're trying to mutate the size of the copy, which will have no effect on the item of the list. This is such a common mistake that the compiler even makes this an error.
If you really are sure that you want to have a mutable value type (hint: you aren't, and you shouldn't have one; you almost certainly just want to have a class here to avoid this problem entirely) then you would need to get the value of the item, mutate it, and then set the item again:
if(symbols[idx].size == 0)
{
var symbol = symbols[idx];
symbol.size = 42;
symbols[idx] = symbol;
}
Your return type on the function is "void" when you should set the return type to the list. That should allow you to change it and return it modified.
I have the following piece of code
List<String> l = new List<String>();
String s = "hello";
l.Add(s);
s = "world";
When I set up some breakpoints and go through the program, after executing the last line, the value in the list is still hello instead of world.
Shouldn't it equal world ? Isn't a string an object, and am I not just inserting a pointer into the list? Later on if I change the string to point to a different value ("world"), why is my list still referencing the old value?
How can I get my desired effect ?
Thanks a lot!
Strings are immutable so that won't work. When you attempt to set into it, you actually drop the pointer to the old string and create a new one under the hood.
To get the desired effect, create a class that wraps a string:
public class SortOfMutableString
{
public string Value {get;set;}
public SortOfMutableString(string s)
{
Value = s;
}
public static implicit operator string(SortOfMutableString s)
{
return s.Value;
}
public static implicit operator SortOfMutableString(string s)
{
return new SortOfMutableString(s);
}
}
And use this in your list. Then references will point to the class, but you can contain the string value inside. To make it even better, override implicit casting to and from string so you don't even need to see that you are talking to a SortOfMutableString.
Refer to Jon Skeet's answer for undoubtedly a very accurate explanation about string's in C#, I'm not even going to bother!
Alternative class names:
PseudoMutableString
ICantBelieveItsNotMutable
HappyAndReferenceableString
You're changing the s reference to refer to a different String instance.
Strings are immutable; it is impossible to change the existing instance that you added to the list.
Instead, you can create a mutable StringHolder class with a writable String property.
No, it shouldn't equal world. The value of the variable s is a reference. When you call l.Add(s), that reference is passed by value to the list. So the list now contains a reference to the string "hello".
You now change the value of s to a reference to the string "world". That doesn't change the list at all.
It's important to distinguish between three very different concepts:
A variable (which has a name and a value)
A reference (a value which allows you to navigate to an object, or null)
An object
So in particular, the list doesn't know anything about the variable s - it knows about the value which was passed into Add; that value happened to be the value of s at the time Add was called, that's all.
You may find these articles helpful:
Values and references
Parameter passing in C#
No, there are two different references involved. One called s and one that's at List[0]. When you say l.Add(s) you are setting the list reference to the same address as s, but then when you assign s to "world", then s will point to the new string, leaving List[0] pointing to the old string.
If you really want to do something like what you are asking, you'd need to wrap the string in another object that contains a string, so that s and List[0] both refer to that object, and then that object's reference to a string can change and both will see it.
public class StringWrapper
{
public string TheString { get; set; }
}
Then you can do:
var s = new StringWrapper { TheString = "Hello" };
var l = new List<StringWrapper>();
l.Add(s);
s.TheString = "World";
And now l[0].TheString will be world too. This works because in this case we are not changing the reference in List[0] or s, but they contents of the object referred to by s and List[0].
A variable is an object reference, not an object itself. s = "world" says "make s refer to the string "World") - it does not in any way affect the string "hello" that s was previously referring to. Furthermore, strings in C# are always immutable. You can, however, make the first list element (which currently refers to "hello") refer to a different string: l[0] = "world".
The other two answers here did a great job of saying why what you tried didnt' work, but you were looking for a solution for your desired effect. Wrap a string (property) inside of an object. Then you can change that string and it will be reflected in the collection.