I've created a struct defined below:
public struct Piece
{
string pieceType;
string pCode;
bool dead;
bool highlighted;
bool specialUsed;
bool moved;
Material defaultMaterial;
}
and in a separate subroutine have created a dictionary which will hold entries of this type:
Dictionary<string, Piece> pieceDict;
I'm attempting to populate it using a foreach loop like so:
GameObject[] pieces = GameObject.FindGameObjectsWithTag("Piece");
foreach (GameObject piece in pieces)
{
string pCode = GetPieceCode(piece);
pieceDict.Add(pCode, new Piece());
pieceDict[pCode].pCode = pCode;
//More properties once I get this working will go here.
}
However, it would appear that something is going wrong as it will not allow me to access the individual properties of the new entry. Getting this error:
Cannot modify the return value of 'Dictionary<string, Piece>.this[string]' because it is not a variable
I have looked at the documentation and can't work out where I'm going wrong, so any help would be appreciated!
I'm using the UnityEngine.
Structs are value types, when you pass a struct, it creates a copy of the struct. This is especially a problem with mutable structs since when you pass the struct to a method (or get it out of a property) the struct is copied by value and then you are modifying the copy, not the original.
The fix is to avoid mutable structs and to use a class instead. Only use structs where the value (of all its fields) is fixed during construction.
Edit
To expand on this a little bit, we can examine the line:
pieceDict[pCode].pCode = pCode;
What is happening here is that the first part, pieceDict[pCode] returns a copy of the value (in the case of a value type), which is then operated on by the .pCode part, which you assign it to pCode, but because you are working on a copy, and not what is stored in the dictionary, it will not be saved. The compiler is smart enough to notice that you are trying to assign to it and that it will be thrown away anyway, so it gives you the error.
In my opinion the error could be better worded, something like "assignment to a copy of a value type does not result in an assignment of the underlying value", because as a new-ish programmer when it says that the assignment fails because its not a variable is a little confusing. The dictionary is a variable, the item that went in is a variable, so its hard to understand why the value is not a variable.
Related
What does GetNode return, a copy or a reference to the real value?
public GraphNode GetNode(int idx)
{
return Nodes[idx];
}
In other words will this code change the real value or it will change a copy returned from GetNode?
GetNode(someIndex).ExtraInfo = something;
Thanks
Depending on wherever GraphNode is a class or struct. In case of a class you'll be changing "real" value. Struct is the opposite.
It depends on your definition of GraphNode.
If it is a class (by reference) it will return the same instance;
or if it is a struct (value-type) then you'll get a new instance.
In other words will this code change the real value or it will change
a copy returned from GetNode?
GetNode(someIndex).ExtraInfo = something;
If GetNode() returns a struct / value type you will actually get a compilation error in C#:
Cannot modify the return value of 'GetNode(someIndex)' because it is
not a variable
This is exactly because the compiler is trying to protect you from changing a copied value that goes nowhere. Your example makes only sense if GetNode() returns a reference type.
The only way to get the example to compile for a value type is by separating retrieval of the return value from the property assignment:
var node = GetNode(someIndex);
node.ExtraInfo = something;
In this case as other posters have mentioned it does depend on whether GetNode() returns a reference type or a value type. For a reference type you are changing the original class instance that GetNode() returns a reference to, for a value type you are modifying a new, copied instance.
The way classes work in C# is that they can be passed around as pointers.
You can set the values inside it, like you have in your example, and it will change the original.
Setting the actual class itself cannot be done without a wrapper though.
It will return a reference (in case GraphNode is a class) to a GraphNode object, so the original object will be altered.
I know it can't be done since using var can only be done for local variables. I'm just wondering if anyone has a theory why the C# team thought this should be so. e.g. what would be wrong with this:
public class SomeClass
{
var someString = "hello"; //not cool
public SomeClass()
{
var someOtherString = "hello"; //cool
}
}
If someString is initialised then it is obviously a string just like someOtherString. Why is there one rule for local variables and another for globals?
Duplicate, hence CW.
See the posting by Eric Lippert:
Let me give you a quick oversimplification of how the C# compiler works. First we run through every source file and do a "top level only" parse. That is, we identify every namespace, class, struct, enum, interface, and delegate type declaration at all levels of nesting. We parse all field declarations, method declarations, and so on. In fact, we parse everything except method bodies; those, we skip and come back to them later.
[...]
if we have "var" fields then the type of the field cannot be determined until the expression is analyzed, and that happens after we already need to know the type of the field.
Its to do with the amount of searching the compiler would have to do resolve the type.
Id' like to create a list of data that will be passed from method to method, however I can't use a struct because the data that will be contained in this list will vary depending on the input.
For example
if (x == 1) {
a = 1
b = true
c = 42
d = "hello"
}
if (x == 2) {
a = 2
b = 'g'
c = "sup"
}
I believe my options are thus:
Create an array or List of strings, and cast the data back to what it originally was from strings. This is messy and could lead to bugs of uninterpretable input, though wouldn't be so bad since it'd all be detected at runtime.
Create a struct for each possibility - Is this even good practice?
Somehow use generics. From what I know, while generics are type-safe yet not type-strict, they must be cast to types before being used. Eg if I wanted a List of items here, I'd need to cast them to strings much like would happen with solution 1, making this useless.
My question then, is which of these options is the best? Or is there an alternate option using some sort of generic type I don't know about? The number of possible variables in each case may change, as with their types. I'd like to be able to return a single List or Array to the calling method, so that it may appropriately deal with the result. It will know how to deal with each group of data based on the value of a, as it will be the 'action choice' identifier. I'm also aware that casting them to objects and back each time is very intensive so I'd rather avoid that.
This is probably pretty simple but it has me stumped...
Since you don't know before hand what the list will contain, it looks like a good case for using an ArrayList.
If you want to get back to the values using a key, consider using a Hashtable.
The general principal in .NET is that every type can be cast to System.Object (although it may involve boxing). You can use a method like
void Foo(params object[] parameters) { ... }
Or use the System.Collections.ArrayList class.
The 'problem' is that when you want to use such a value, you will need code like:
if (parameters[i] is string)
{
string s = (string) parameters[i];
...
}
Sorry, this is not a code related answer: there may be a faulty design hidden behind such a construct. Make sure you know what you are doing, otherwise things might fire back.
If not knowing the type of the fields you use beforehand really is required, this calls for an approach that saves the data with their type, like
struct foo {
private object _value;
private string _type;
foo(string myType, object myValue) {
_value = myValue;
_type = myType;
}
}
and then using Generics to handle the business logic.
Basically you need a list typed to Object, and then yes, you're in a mode of casting back.
My question is, structurally, how will you know what indexes are of which type? This sounds like a painful solution at best.
If you really need to store differing types in the list, perhaps try a struct which contains a member of each type, as well as a flag indicating which data type is represented. Then use a generic collection for that struct. Something like (off the top of my head)
struct FooType
{
public string StringValue;
public bool BoolValue;
public int IntValue;
public char CharValue;
public string DataType;
// You'd probably want constructors too
}
Then the generic list:
var values = new List<FooType>();
Now you can add and remove entries in the list using that type, which would then indicate what the core data really is.
I still don't like the answer; it sounds like your design may be trying to do too much and there may be refactoring opportunities, but since I don't see much more of your code or intent, all I can do is answer what you've asked. :)
You could represent the data items using a Dictionary/Hashtable and then add these dictionaries to a List.
You could also add extra type information into the dictionary value if needed.
If I write:
SomeType simpleName = classWithLongName.otherLongName;
And then use "simpleName" instead of "classWithLongName.otherLongName", will this change the program in any way (for instance performance wise)?
What does the compiler do with this? Does it copy+paste "classWithLongName.otherLongName", everywhere I use "simpleName".
No, the C# compiler doesn't translate a call to "simpleName" to be the same as copying and pasting "classWithLongName.otherLongName". The difference could be profound or simply semantic, but what you're doing is assigning the value from classWithLongName.otherLongName to simpleName. Whether the type is a value type or a reference type will determine exactly what happens and what will happen if you manipulate that value, but you're not creating a function pointer or delegate in doing that.
Whether it will have an effect on performance really isn't something that can be answered here, other than to say that it won't have a NEGATIVE effect. We can't say if it will have a positive effect, since that would depend on what actually happens when you call classWithLongName.otherLongName. If that's an expensive operation, then this could make it faster, but the downside would be that any differences in value upon subsequent calls to classWithLongName.otherLongName wouldn't be reflected if you cached its value in simpleName.
It depends what "otherLongName" is actually doing. If it's a property, then the difference is between executing the property several times or only executing it once. That may or may not change the behaviour of the program in a significant way, depending on what it's doing.
The compiler is only allowed to cache the value and re-use it itself when you always type "classWithLongName.otherLongName" if it knows that the value will not change in the course. However, this is seldom the case.
Therefore, if "classWithLongName.otherLongName" does perform some computation, you'll usually get better performance by caching it manually in a local variable as you suggested. However, keep in mind that you are working with a cached value and that changes in the original value or property will not be reflected on your cached value.
The length of the name however is just metadata and has no influence whatsoever on runtime performance, since the name is already resolved to an internal handle during compilation.
Is this a question about instances or classes?
For instance
namespace MyCompany.MyApp.LongNamespaceName
{
public class MyClassWithALongName {
public SomeType AnInstanceProperty {get;set;}
public static SomeType AStaticProperty {get { ... }}
}
}
Now:
//this gets the static property
SomeType simpleName = MyClassWithALongName.AStaticProperty;
Alternatively:
MyClassWithALongName anInstanceWithALongName = new MyClassWithALongName();
//this gets the instance property
SomeType simpleName = anInstanceWithALongName.AnInstanceProperty;
These will behave in different ways.
There's another case here though, you can create an alias for the actual name of the class:
using simpleName = MyCompany.MyApp.LongNamespaceName.MyClassWithALongName;
...
simpleName anInstance = new simpleName ();
If classWithLongName.otherLongName is a property, than changes to simpleName will NOT change classWithLongName.otherLongName.
If classWithLongName.otherLongName is a public data member (a field) of a value type, than changes to simpleName will NOT change classWithLongName.otherLongName.
If classWithLongName.otherLongName is a public data member (a field) of a reference type, than changes to simpleName WILL change classWithLongName.otherLongName.
Assuming your type is an object (reference) type then simpleName will end up containing a reference to the object returned by classWithLongName.otherLongName. If you are then going to make lots of calls to properties on that object then you may get a performance improvement, especially if otherLongName is a property as opposed to a field.
You can always make it a function.
SomeType simpleName() { return classWithLongName.otherLongName; }
Consider this:
List<MyClass> obj_list = get_the_list();
foreach( MyClass obj in obj_list )
{
obj.property = 42;
}
Is obj a reference to the corresponding object within the list so that when I change the property the change will persist in the object instance once constructed somewhere?
Yes, obj is a reference to the current object in the collection (assuming MyClass is in fact a class). If you change any properties via the reference, you're changing the object, just like you would expect.
Be aware however, that you cannot change the variable obj itself as it is the iteration variable. You'll get a compile error if you try. That means that you can't null it and if you're iterating value types, you can't modify any members as that would be changing the value.
The C# language specification states (8.8.4)
"The iteration variable corresponds to
a read-only local variable with a
scope that extends over the embedded
statement."
Yes, until you change the generic type from List to IEnumerable..
You've asked 2 different questions here, lets take them in order.
Does a foreach loop iterate by reference?
If you mean in the same sense as a C++ for loop by reference, then no. C# does not have local variable references in the same sense as C++ and hence doesn't support this type of iteration.
Will the change be persisted
Assuming that MyClass is a reference type, the answer is yes. A class is a reference type in .Net and hence the iteration variable is a reference to the one variable, not a copy. This would not be true for a value type.
Well, it happened to me that my changes were not updated in a foreach loop when I iterated through var collection:
var players = this.GetAllPlayers();
foreach (Player player in players)
{
player.Position = 1;
}
When I changed var to List it started working.
You can in this instance (using a List<T>) but if you were to be iterating over the generic IEnumerable<T> then it becomes dependant on its implementation.
If it was still a List<T> or T[] for instance, all would work as expected.
The big gotcha comes when you are working with an IEnumerable<T> that was constructed using yield. In this case, you can no longer modify properties of T within an iteration and expect them to be present if you iterate the same IEnumerable<T> again.
Maybe it's interesting for you to lean that by version C# 7.3 it's possible to change values by reference provided that the enumerator's Current property returns a reference Type. The following would be valid (verbatim copy from the MS docs):
Span<int> storage = stackalloc int[10];
int num = 0;
foreach (ref int item in storage)
{
item = num++;
}
Read more about this new feature at
C# foreach statement | Microsoft Docs.
this is true as long as it is not a struct.
Well, without understanding exactly what you mean by "Iterate by reference", I can't answer specifically yes or no, but I can say that what's going on under the surface is that the .net framework is constructing an "enumerator" class for each time client code calls a foreach, for the life of the foreach, that maintains a reference pointer into the collection being iterated over, and each time your foreach iterates, ir "delivers" one item and "increments" the pointer or reference in the enumerator to the next item...
This happens regardless of whether the items in the collection you are iterating over are values types or reference types.
obj is a reference to an item inside the List, hence if you change it's value it will persist. Now what you should be concerned about is whether or not get_the_list(); is making a deep copy of the List or returning the same instance.
Yes, that's also why you cannot alter the enumerable object in the context of the foreach statement.