Ok.. I have a really awkward problem that I believe is related with how C# handles value types vs reference types but I'm just not sure what exactly the bug is.
public partial class LogicSimulationViewerForm : Form
{
private Dictionary<string, PointStruct> pointValues;
private void SearchPoint(string code)
{
ReadDefaultPointValuesResponse result = ddcdao.ReadDefaultPoint(points);
pointValues = new Dictionary<string, PointStruct>();
for (int j = 0; j < result.pointidentifier.Length; j++)
{
if (!pointValues.ContainsKey(result.pointidentifier[j]))
{
PointStruct ps = new PointStruct();
ps.name = "Random String";
ps.pointidentifier = result.pointidentifier[j];
ps.outofservice = result.outofservice[j];
pointValues.Add(result.pointidentifier[j], ps);
...
pointValues is stored as a private field in a class. Now in the same class but in a different function, if I try to do the following:
PointStruct ps = pointValues[s];
MessageBox.Show(ps.name);
MessageBox.Show(ps.pointidentifier);
MessageBox.Show(ps.outofservice);
The ps.pointidentifier and ps.outofservice is displayed correctly but ps.name is always returned as null no matter what I do. How can I fix this issue?
Edit: Upon request, I am adding more code to further illustrate the problem:
public struct PointStruct
{
public string pointidentifier;
public string affect;
public string outofservice;
public string priorityarray;
public string pointtype;
public string alarmstate;
public string correctvalue;
public string presentvalue;
public string name;
public string test;
}
As long as there is no voodoo (explicit field layouts, property indirection, etc), there is absolutely no reason why a field should wipe itself, regardless of whether it is a class or a struct.
If it was a class, we could perhaps put that down to a careless update somewhere else, i.e.
var foo = pointValues[key];
// snip 2000 lines
foo.name = newValue; // which happens to be null
which would of course update the same fundamental object as the one referenced by the dictionary. But that doesn't apply to a struct, since the copies are separate (unless updating directly in an array).
The only way I can see of causing that, given that you state that pointValues.Add(...) is only used in one place, is that you are overwriting it elsewhere via the indexer:
pointValues[key] = newValueWithANullName;
All that said, though; unless you have some very specific reasons, there is very little purpose for PointStruct to be a struct. That looks to me like it should be a class. It is very "fat" for a struct. Also; in most cases, structs should be immutable.
It would be very surprising for a struct stored in a Dictionary to have any of its string fields change to hold anything other than the value they held when the struct was stored in the Dictionary. Is your name actually a literal "Random String", or are you using that literal to represent some other function? Have you confirmed that the function in question is actually working?
Unlike some people here, I very much like mutable structs, despite the limitations in .net's support for them, because they allow the owner of a struct to control who can mutate it. By contrast, if a reference to a mutable class object has ever been exposed to outside code, there's no telling when or by whom it might be altered. The fact that PointStruct is a struct means that there's a 99.44% chance that the field contents of the struct you are retrieving from the Dictionary are the same as the field contents the struct had when it was stored. Add a check to ensure that the Name field is non-null whenever it's stored to the Dictionary and you'll almost certainly find your problem. A much better situation to be in than with a mutable class, where you'd have to inspect outside code to ensure that nothing's altering it.
Addendum
There is one evil thing about mutable structs, which is that if you have any struct members, other than a constructor or property setters, which mutate this, an attempt to use such a member in read-only contexts will generate bogus code but won't generate any compiler diagnostics. Languages and frameworks which properly support value-type semantics require that members which mutate this be tagged as such, and forbid the use of such members in read-only contexts, but unfortunately .net has no such tagging. It simply guesses that constructors and property setters will mutate the underlying struct, while getters and other methods will not. If the `Name field is filled in with a struct method, e.g.
void ComputeName(void)
{
Name = someRandomString();
}
I would strongly suggest that you replace it with a static method:
void ComputeName(ref theStruct)
{
theStruct.Name = someRandomString();
}
If the former function is called on a read-only instance of PointStruct, the compiler will--as noted--compile without complaint but the resulting code won't work. Attempting to pass a read-only instance of PointStruct to the latter, however, will cause a compiler error.
Related
I have a list of structs and I want to change one element. For example :
MyList.Add(new MyStruct("john");
MyList.Add(new MyStruct("peter");
Now I want to change one element:
MyList[1].Name = "bob"
However, whenever I try and do this I get the following error:
Cannot modify the return value of
System.Collections.Generic.List.this[int]‘ because it is not
a variable
If I use a list of classes, the problem doesn't occur.
I guess the answer has to do with structs being a value type.
So, if I have a list of structs should I treat them as read-only? If I need to change elements in a list then I should use classes and not structs?
Not quite. Designing a type as class or struct shouldn't be driven by your need to store it in collections :) You should look at the 'semantics' needed
The problem you're seeing is due to value type semantics. Each value type variable/reference is a new instance. When you say
Struct obItem = MyList[1];
what happens is that a new instance of the struct is created and all members are copied one by one. So that you have a clone of MyList[1] i.e. 2 instances.
Now if you modify obItem, it doesn't affect the original.
obItem.Name = "Gishu"; // MyList[1].Name still remains "peter"
Now bear with me for 2 mins here (This takes a while to gulp down.. it did for me :)
If you really need structs to be stored in a collection and modified like you indicated in your question, you'll have to make your struct expose an interface (However this will result in boxing). You can then modify the actual struct via an interface reference, which refers to the boxed object.
The following code snippet illustrates what I just said above
public interface IMyStructModifier
{
String Name { set; }
}
public struct MyStruct : IMyStructModifier ...
List<Object> obList = new List<object>();
obList.Add(new MyStruct("ABC"));
obList.Add(new MyStruct("DEF"));
MyStruct temp = (MyStruct)obList[1];
temp.Name = "Gishu";
foreach (MyStruct s in obList) // => "ABC", "DEF"
{
Console.WriteLine(s.Name);
}
IMyStructModifier temp2 = obList[1] as IMyStructModifier;
temp2.Name = "Now Gishu";
foreach (MyStruct s in obList) // => "ABC", "Now Gishu"
{
Console.WriteLine(s.Name);
}
HTH. Good Question.
Update: #Hath - you had me running to check if I overlooked something that simple. (It would be inconsistent if setter properties dont and methods did - the .Net universe is still balanced :)
Setter method doesn't work
obList2[1] returns a copy whose state would be modified. Original struct in list stays unmodified. So Set-via-Interface seems to be only way to do it.
List<MyStruct> obList2 = new List<MyStruct>();
obList2.Add(new MyStruct("ABC"));
obList2.Add(new MyStruct("DEF"));
obList2[1].SetName("WTH");
foreach (MyStruct s in obList2) // => "ABC", "DEF"
{
Console.WriteLine(s.Name);
}
MyList[1] = new MyStruct("bob");
structs in C# should almost always be designed to be immutable (that is, have no way to change their internal state once they have been created).
In your case, what you want to do is to replace the entire struct in specified array index, not to try to change just a single property or field.
It's not so much that structs are "immutable."
The real underlying issue is that structs are a Value type, not a Reference type. So when you pull out a "reference" to the struct from the list, it is creating a new copy of the entire struct. So any changes you make on it are changing the copy, not the original version in the list.
Like Andrew states, you have to replace the entire struct. As that point though I think you have to ask yourself why you are using a struct in the first place (instead of a class). Make sure you aren't doing it around premature optimization concerns.
In .Net 5.0, you can use CollectionsMarshal.AsSpan() (source, GitHub issue) to get the underlying array of a List<T> as a Span<T>. Note that items should not be added or removed from the List<T> while the Span<T> is in use.
var listOfStructs = new List<MyStruct> { new MyStruct() };
Span<MyStruct> spanOfStructs = CollectionsMarshal.AsSpan(listOfStructs);
spanOfStructs[0].Value = 42;
Assert.Equal(42, spanOfStructs[0].Value);
struct MyStruct { public int Value { get; set; } }
This works because the Span<T> indexer uses a C# 7.0 feature called ref returns. The indexer is declared with a ref T return type, which provides semantics like that of indexing into arrays, returning a reference to the actual storage location.
In comparison the List<T> indexer is not ref returning instead returning a copy of what lives at that location.
Keep in mind that this is still unsafe: if the List<T> reallocates the array, the Span<T> previously returned by CollectionsMarshal.AsSpan won't reflect any further changes to the List<T>. (Which is why the method is hidden in the System.Runtime.InteropServices.CollectionsMarshal class.)
Source
There is nothing wrong with structs that have exposed fields, or that allow mutation via property setters. Structs which mutate themselves in response to methods or property getters, however, are dangerous because the system will allow methods or property getters to be called on temporary struct instances; if the methods or getters make changes to the struct, those changes will end up getting discarded.
Unfortunately, as you note, the collections built into .net are really feeble at exposing value-type objects contained therein. Your best bet is usually to do something like:
MyStruct temp = myList[1];
temp.Name = "Albert";
myList[1] = temp;
Somewhat annoying, and not at all threadsafe. Still an improvement over a List of a class type, where doing the same thing might require:
myList[1].Name = "Albert";
but it might also require:
myList[1] = myList[1].Withname("Albert");
or maybe
myClass temp = (myClass)myList[1].Clone();
temp.Name = "Albert";
myList[1] = temp;
or maybe some other variation. One really wouldn't be able to know unless one examined myClass as well as the other code that put things in the list. It's entirely possible that one might not be able to know whether the first form is safe without examining code in assemblies to which one does not have access. By contrast, if Name is an exposed field of MyStruct, the method I gave for updating it will work, regardless of what else MyStruct contains, or regardless of what other things may have done with myList before the code executes or what they may expect to do with it after.
In addition to the other answers, I thought it could be helpful to explain why the compiler complains.
When you call MyList[1].Name, unlike an array, the MyList[1] actually calls the indexer method behind the scenes.
Any time a method returns an instance of a struct, you're getting a copy of that struct (unless you use ref/out).
So you're getting a copy and setting the Name property on a copy, which is about to be discarded since the copy wasn't stored in a variable anywhere.
This tutorial describes what's going on in more detail (including the generated CIL code).
As of C#9, I am not aware of any way to pull a struct by reference out of a generic container, including List<T>. As Jason Olson's answer said:
The real underlying issue is that structs are a Value type, not a Reference type. So when you pull out a "reference" to the struct from the list, it is creating a new copy of the entire struct. So any changes you make on it are changing the copy, not the original version in the list.
So, this can be pretty inefficient. SuperCat's answer, even though it is correct, compounds that inefficiency by copying the updated struct back into the list.
If you are interested in maximizing the performance of structs, then use an array instead of List<T>. The indexer in an array returns a reference to the struct and does not copy the entire struct out like the List<T> indexer. Also, an array is more efficient than List<T>.
If you need to grow the array over time, then create a generic class that works like List<T>, but uses arrays underneath.
There is an alternative solution. Create a class that incorporates the structure and create public methods to call the methods of that structure for the required functionality. Use a List<T> and specify the class for T. The structure may also be returned via a ref returns method or ref property that returns a reference to the structure.
The advantage of this approach is that it can be used with any generic data structure, like Dictionary<TKey, TValue>. When pulling a struct out of a Dictionary<TKey, TValue>, it also copies the struct to a new instance, just like List<T>. I suspect that this is true for all C# generic containers.
Code example:
public struct Mutable
{
private int _x;
public Mutable(int x)
{
_x = x;
}
public int X => _x; // Property
public void IncrementX() { _x++; }
}
public class MutClass
{
public Mutable Mut;
//
public MutClass()
{
Mut = new Mutable(2);
}
public MutClass(int x)
{
Mut = new Mutable(x);
}
public ref Mutable MutRef => ref Mut; // Property
public ref Mutable GetMutStruct()
{
return ref Mut;
}
}
private static void TestClassList()
{
// This test method shows that a list of a class that holds a struct
// may be used to efficiently obtain the struct by reference.
//
var mcList = new List<MutClass>();
var mClass = new MutClass(1);
mcList.Add(mClass);
ref Mutable mutRef = ref mcList[0].MutRef;
// Increment the x value defined in the struct.
mutRef.IncrementX();
// Now verify that the X values match.
if (mutRef.X != mClass.Mut.X)
Console.Error.WriteLine("TestClassList: Error - the X values do not match.");
else
Console.Error.WriteLine("TestClassList: Success - the X values match!");
}
Output on console window:
TestClassList: Success - the X values match!
For the following line:
ref Mutable mutRef = ref mcList[0].MutRef;
I initially and inadvertently left out the ref after the equal sign. The compiler didn't complain, but it did produce a copy of the struct and the test failed when it ran. After adding the ref, it ran correctly.
Ok this is more curiosity than practical requirement.
Let's say I have this class:
public sealed class Entity
{
int value;
Entity()
{
}
public static implicit operator Entity(int x)
{
return new Entity { value = x };
}
}
I don't want this class to be instantiated outside the class and this works. But here Entitys will be referenced and not copied like value types. This class being so small I want it to behave likes value types. Also if I compare two instances of Entitys like e1 == e2 it's going to give reference equality (ok I can overload == but that's more work). So I would make it a struct:
public struct Entity
{
int value;
public static implicit operator Entity(int x)
{
return new Entity { value = x };
}
}
But now someone can do new Entity() easily.
My question is there a way I can have a type (struct/class) that
1) will be copied every time the value is accessed, so that
Entity e1 = 1;
Entity e2 = e1;
ReferenceEquals(e1, e2); // prints false, just like any value type
2) has value semantics when comparing for equality
Entity e1 = 1;
Entity e2 = 1;
e1.Equals(e2); // prints true
// this is easy though by overriding default equality.
3) is not instantiable outside the scope of the type:
Entity e = new Entity(); // should not compile
Basically close to an enum's behaviour. I can certainly live without it, just learning.
Its not possible to do re-define or try to hide the default constructor for a struct in C# because the C# compiler routinely comples code based on the assumption that the parameterless constructor does nothing. See Why can't I define a default constructor for a struct in .NET?.
Your options are either
Use a class
Ensure that the default value for your struct has some meaning (e.g. in your case the int will be initialised to 0) - this is what enums do (recall that enums are initialised to 0).
My recommendation would be to use a class until you identify that there is a performance problem.
A type has no say over what can happen to storage locations of that type. Storage locations of any type can come into existence, or have their contents overwritten with the content of other locations of the same type, without the type itself having any say in the matter. A storage location of a reference type holds a class reference; when it comes into existence, it will be a null reference. The only way a storage location of a reference type will hold anything other than a null reference is if an instance of that type is created and--outside some scenarios involving Full Trust Code and reflection--a reference type will have full control over the circumstances where that can occur.
By contrast, a storage location of a value type is an instance of that type. Consequently, there is no way a structure can control how one comes into existence. What a structure can control--to a limited extent--is what values its fields can contain. In limited trust scenarios, a private struct field can only assume a non-default value if the code for the struct writes that value to some instance of the struct (if the value gets written to any instance of the struct, even limited-trust code with access to that instance may be able to use multi-threaded overlapped reads and writes to produce a struct with a combination of field values which the struct itself would never create itself).
I would suggest creating a struct with a single private field of a class type. You cannot prevent code from creating a default(Entity), but that doesn't mean you have to allow code to do anything useful with one. Its Equals method should work well enough to return true when comparing to another default instance, and false when compared with anything else, and GetHashCode should return the same value for all default instances, but any other methods could throw an exception.
Make all of the constructors (besides the static constructor) private for the class Entity. protected would work too, but since its sealed, that doesn't make a lot of sense.
In C#, an anonymous type can be as follows:
method doStuff(){
var myVar = new {
a = false,
b = true
}
if (myVar.a)
{
// Do stuff
}
}
However, the following will not compile:
method doStuff(){
var myVar = new {
a = false,
b = true
}
if (myVar.a)
{
myVar.b = true;
}
}
This is because myVar's fields are read-only and cannot be assigned to. It seems wanting to do something like the latter is fairly common; perhaps the best solution I've seen is to just define a struct outside the method.
However, is there really no other way to make the above block work? The reason it bothers me is, myVar is a local variable of this field, so it seems like it should only be referred to inside the method that uses it. Besides, needing to place the struct outside of the method can make the declaration of an object quite far from its use, especially in a long method.
Put in another way, is there an alternative to anonymous types which will allow me to define a "struct" like this (I realize struct exists in C# and must be defined outside of a method) without making it read-only? If no, is there something fundamentally wrong with wanting to do this, and should I be using a different approach?
No, you'll have to create your own class or struct to do this (preferrably a class if you want it to be mutable - mutable structs are horrible).
If you don't care about Equals/ToString/GetHashCode implementations, that's pretty easy:
public class MyClass {
public bool Foo { get; set; }
public bool Bar { get; set; }
}
(I'd still use properties rather than fields, for various reasons.)
Personally I usually find myself wanting an immutable type which I can pass between methods etc - I want a named version of the existing anonymous type feature...
Is there an alternative to anonymous types which will allow me to concisely define a simple "record" type like this without making it read-only?
No. You'll have to make a nominal type.
If no, is there something fundamentally wrong with wanting to do this?
No, it's a reasonable feature that we have considered before.
I note that in Visual Basic, anonymous types are mutable if you want them to be.
The only thing that is really "fundamentally wrong" about a mutable anonymous type is that it would be dangerous to use one as a hash key. We designed anonymous types with the assumptions that (1) you're going to use them as the keys in equijoins in LINQ query comprehensions, and (2) in LINQ-to-Objects and other implementations, joins will be implemented using hash tables. Therefore anonymous types should be useful as hash keys, and mutable hash keys are dangerous.
In Visual Basic, the GetHashCode implementation does not consume any information from mutable fields of anonymous types. Though that is a reasonable compromise, we simply decided that in C# the extra complexity wasn't worth the effort.
In C# 7 we can leverage named tuples to do the trick:
(bool a, bool b) myVar = (false, true);
if (myVar.a)
{
myVar.b = true;
}
You won't be able to get the nice initialization syntax but the ExpandoObject class introduced in .NET 4 would serve as a viable solution.
dynamic eo = new ExpandoObject();
eo.SomeIntValue = 5;
eo.SomeIntValue = 10; // works fine
For the above types of operation, you should define your own mutable STRUCT. Mutable structs may pose a headache for compiler writers like Eric Lippert, and there are some unfortunate limitations in how .net handles them, but nonetheless the semantics of mutable "Plain Old Data" structs (structs in which all fields are public, and the only public functions which write this are constructors, or are called exclusively from constructors) offer far clearer semantics than can be achieved via classes.
For example, consider the following:
struct Foo {
public int bar;
...other stuff;
}
int test(Action<Foo[]> proc1, Action<Foo> proc2)
{
foo myFoos[] = new Foo[100];
proc1(myFoos);
myFoos[4].bar = 9;
proc2(myFoos[4]); // Pass-by-value
return myFoos[4].bar;
}
Assuming there's no unsafe code and that the passed-in delegates can be called and will return in finite time, what will test() return? The fact that Foo is a struct with a public field bar is sufficient to answer the question: it will return 9, regardless of what else appears in the declaration of Foo, and regardless of what functions are passed in proc1 and proc2. If Foo were a class, one would have to examine every single Action<Foo[]> and Action<Foo> that exists, or will ever exist, to know what test() would return. Determining that Foo is a struct with public field bar seems much easier than examining all past and future functions that might get passed in.
Struct methods which modify this are handled particularly poorly in .net, so if one needs to use a method to modify a struct, it's almost certainly better to use one of these patterns:
myStruct = myStruct.ModifiedInSomeFashion(...); // Approach #1
myStructType.ModifyInSomeFashion(ref myStruct, ...); // Approach #2
than the pattern:
myStruct.ModifyInSomeFashion(...);
Provided one uses the above approach to struct-modifying patterns, however, mutable structs have the advantage of allowing code which is both more efficient and easier to read than immutable structs or immutable classes, and is much less trouble-prone than mutable classes. For things which represent an aggregation of values, with no identity outside the values they contain, mutable class types are often the worst possible representation.
I find it really annoying that you can't set anonymous properties as read/write as you can in VB - often I want to return data from a database using EF/LINQ projection, and then do some massaging of the data in c# that can't be done at the database for whatever reason. The easiest way to do this is to iterate over existing anonymous instances and update properties as you go. NOTE this is not so bad now in EF.Core, as you can mix db functions and .net functions in a single query finally.
My go-to workaround is to use reflection and will be frowned upon and down-voted but works; buyer beware if the underlying implementation changes and all your code breaks.
public static class AnonClassHelper {
public static void SetField<T>(object anonClass, string fieldName, T value) {
var field = anonClass.GetType().GetField($"<{fieldName}>i__Field", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
field.SetValue(anonClass, value);
}
}
// usage
AnonClassHelper.SetField(inst, nameof(inst.SomeField), newVal);
An alternative I have used when dealing with strings is to make properties of type StringBuilder, then these individual properties will be settable via the StringBuilder methods after you have an instance of your anonymous type.
I know it is really old question but how about replacing whole anonymous
object:
`
if (myVar.a)
{
myVar = new
{ a = false, b = true };
}
`
I have been trying to wrap my head around this FXCop violation "DoNotDeclareReadOnlyMutableReferenceTypes"
MSDN: http://msdn.microsoft.com/en-us/library/ms182302%28VS.80%29.aspx
Code from MSDN which would cause this violation:
namespace SecurityLibrary
{
public class MutableReferenceTypes
{
static protected readonly StringBuilder SomeStringBuilder;
static MutableReferenceTypes()
{
SomeStringBuilder = new StringBuilder();
}
}
}
From Jon's answer here and here , I understand that the field holding the reference to the object (in this case SomeStringBuilder) is readonly and not the object itself (which is created by new StringBuilder() )
So taking this example, how would I change the object itself, once the field has a reference to it ? I like Eric Lippert's example of how the readonly array can be changed, and would like to see something similar for any other mutable reference type
readonly means you can't change the reference post-construction.
The official FXCop stance is that it recommends that only types that can't be modified should be declared readonly. Therefore something like a string is okay because the value of the object can't be changed. However the value of StringBuilder can changed but making it readonly only prevents you from assigning the field to a different StringBuilder instance or null after the constructor runs.
I disagree with FXCop on this rule. As long as one understands that this is simply an enforcement that the reference may not change post-construction then there is no confusion.
Note that value-types are made immutable by the readonly keyword but reference types are not.
namespace SecurityLibrary
{
public class MutableReferenceTypes
{
static protected readonly StringBuilder SomeStringBuilder;
static MutableReferenceTypes()
{
// allowed
SomeStringBuilder = new StringBuilder();
}
void Foo()
{
// not allowed
SomeStringBuilder = new StringBuilder();
}
void Bar()
{
// allowed but FXCop doesn't like this
SomeStringBuilder.AppendLine("Bar");
}
}
}
As the MutableReferenceTypes class is presented in the question, you can't really mutate it from any outside caller since the SomeStringBuilder field is private.
However, the class itself could mutate the field. It doesn't currently, but it could in a later iteration.
Here's an example method:
public static void Mutate()
{
SomeStringBuilder.AppendLine("Foo");
}
Calling the Mutate method will mutate the class because SomeStringBuilder will now have changed.
Immutability is not only about the current incarnation of your code, but also about protecting yourself from future mistakes. Not that all classes need to be immutable, but it's safest to stay consistent if you elect to create an immutable type.
.Net has a list of immutable reference types that are allowed here, StringBuilder isn't one of them.
The complaint is that what you're constructing isn't immutable, though the static constructor is called once and the class is initialized once, that's all that stays the same, the rest is mutable. One thread may call .Append(), then another...you see how the string builder itself mutates and isn't really readonly, because it constantly changes states/mutates.
Declaring it readonly is really a misnomer since the object referenced there itself is constantly changing.
You cannot change the reference, but any call on the (mutable) object changes its state.
Therefore, since the SomeStringBuilder (in this example) is mutable itself, its content may change, which may be misleading for users of the class because it is therefore not really "read-only".
Basically, readonlydoes not in any way guarantee that the object foes not change, it merely does say that the reference does not change.
You would not change the value of the object. That's the point of the rule. Any field declared as readonly should infact be that, readonly. Having a readonly mutable reference is an oxymoron. If you Can change the value of what the field "points" to then it's not really readonly any more. There's really no functional difference between assigning the value of all members of some object A to some object B represented by a field or simply assigning A to that field (when they are of the same type) however only one of Them is valid when the field is declared readonly but since you Can effectively chage the value of what's being represented by the field it is as already stated not really readonly
I have a list of structs and I want to change one element. For example :
MyList.Add(new MyStruct("john");
MyList.Add(new MyStruct("peter");
Now I want to change one element:
MyList[1].Name = "bob"
However, whenever I try and do this I get the following error:
Cannot modify the return value of
System.Collections.Generic.List.this[int]‘ because it is not
a variable
If I use a list of classes, the problem doesn't occur.
I guess the answer has to do with structs being a value type.
So, if I have a list of structs should I treat them as read-only? If I need to change elements in a list then I should use classes and not structs?
Not quite. Designing a type as class or struct shouldn't be driven by your need to store it in collections :) You should look at the 'semantics' needed
The problem you're seeing is due to value type semantics. Each value type variable/reference is a new instance. When you say
Struct obItem = MyList[1];
what happens is that a new instance of the struct is created and all members are copied one by one. So that you have a clone of MyList[1] i.e. 2 instances.
Now if you modify obItem, it doesn't affect the original.
obItem.Name = "Gishu"; // MyList[1].Name still remains "peter"
Now bear with me for 2 mins here (This takes a while to gulp down.. it did for me :)
If you really need structs to be stored in a collection and modified like you indicated in your question, you'll have to make your struct expose an interface (However this will result in boxing). You can then modify the actual struct via an interface reference, which refers to the boxed object.
The following code snippet illustrates what I just said above
public interface IMyStructModifier
{
String Name { set; }
}
public struct MyStruct : IMyStructModifier ...
List<Object> obList = new List<object>();
obList.Add(new MyStruct("ABC"));
obList.Add(new MyStruct("DEF"));
MyStruct temp = (MyStruct)obList[1];
temp.Name = "Gishu";
foreach (MyStruct s in obList) // => "ABC", "DEF"
{
Console.WriteLine(s.Name);
}
IMyStructModifier temp2 = obList[1] as IMyStructModifier;
temp2.Name = "Now Gishu";
foreach (MyStruct s in obList) // => "ABC", "Now Gishu"
{
Console.WriteLine(s.Name);
}
HTH. Good Question.
Update: #Hath - you had me running to check if I overlooked something that simple. (It would be inconsistent if setter properties dont and methods did - the .Net universe is still balanced :)
Setter method doesn't work
obList2[1] returns a copy whose state would be modified. Original struct in list stays unmodified. So Set-via-Interface seems to be only way to do it.
List<MyStruct> obList2 = new List<MyStruct>();
obList2.Add(new MyStruct("ABC"));
obList2.Add(new MyStruct("DEF"));
obList2[1].SetName("WTH");
foreach (MyStruct s in obList2) // => "ABC", "DEF"
{
Console.WriteLine(s.Name);
}
MyList[1] = new MyStruct("bob");
structs in C# should almost always be designed to be immutable (that is, have no way to change their internal state once they have been created).
In your case, what you want to do is to replace the entire struct in specified array index, not to try to change just a single property or field.
It's not so much that structs are "immutable."
The real underlying issue is that structs are a Value type, not a Reference type. So when you pull out a "reference" to the struct from the list, it is creating a new copy of the entire struct. So any changes you make on it are changing the copy, not the original version in the list.
Like Andrew states, you have to replace the entire struct. As that point though I think you have to ask yourself why you are using a struct in the first place (instead of a class). Make sure you aren't doing it around premature optimization concerns.
In .Net 5.0, you can use CollectionsMarshal.AsSpan() (source, GitHub issue) to get the underlying array of a List<T> as a Span<T>. Note that items should not be added or removed from the List<T> while the Span<T> is in use.
var listOfStructs = new List<MyStruct> { new MyStruct() };
Span<MyStruct> spanOfStructs = CollectionsMarshal.AsSpan(listOfStructs);
spanOfStructs[0].Value = 42;
Assert.Equal(42, spanOfStructs[0].Value);
struct MyStruct { public int Value { get; set; } }
This works because the Span<T> indexer uses a C# 7.0 feature called ref returns. The indexer is declared with a ref T return type, which provides semantics like that of indexing into arrays, returning a reference to the actual storage location.
In comparison the List<T> indexer is not ref returning instead returning a copy of what lives at that location.
Keep in mind that this is still unsafe: if the List<T> reallocates the array, the Span<T> previously returned by CollectionsMarshal.AsSpan won't reflect any further changes to the List<T>. (Which is why the method is hidden in the System.Runtime.InteropServices.CollectionsMarshal class.)
Source
There is nothing wrong with structs that have exposed fields, or that allow mutation via property setters. Structs which mutate themselves in response to methods or property getters, however, are dangerous because the system will allow methods or property getters to be called on temporary struct instances; if the methods or getters make changes to the struct, those changes will end up getting discarded.
Unfortunately, as you note, the collections built into .net are really feeble at exposing value-type objects contained therein. Your best bet is usually to do something like:
MyStruct temp = myList[1];
temp.Name = "Albert";
myList[1] = temp;
Somewhat annoying, and not at all threadsafe. Still an improvement over a List of a class type, where doing the same thing might require:
myList[1].Name = "Albert";
but it might also require:
myList[1] = myList[1].Withname("Albert");
or maybe
myClass temp = (myClass)myList[1].Clone();
temp.Name = "Albert";
myList[1] = temp;
or maybe some other variation. One really wouldn't be able to know unless one examined myClass as well as the other code that put things in the list. It's entirely possible that one might not be able to know whether the first form is safe without examining code in assemblies to which one does not have access. By contrast, if Name is an exposed field of MyStruct, the method I gave for updating it will work, regardless of what else MyStruct contains, or regardless of what other things may have done with myList before the code executes or what they may expect to do with it after.
In addition to the other answers, I thought it could be helpful to explain why the compiler complains.
When you call MyList[1].Name, unlike an array, the MyList[1] actually calls the indexer method behind the scenes.
Any time a method returns an instance of a struct, you're getting a copy of that struct (unless you use ref/out).
So you're getting a copy and setting the Name property on a copy, which is about to be discarded since the copy wasn't stored in a variable anywhere.
This tutorial describes what's going on in more detail (including the generated CIL code).
As of C#9, I am not aware of any way to pull a struct by reference out of a generic container, including List<T>. As Jason Olson's answer said:
The real underlying issue is that structs are a Value type, not a Reference type. So when you pull out a "reference" to the struct from the list, it is creating a new copy of the entire struct. So any changes you make on it are changing the copy, not the original version in the list.
So, this can be pretty inefficient. SuperCat's answer, even though it is correct, compounds that inefficiency by copying the updated struct back into the list.
If you are interested in maximizing the performance of structs, then use an array instead of List<T>. The indexer in an array returns a reference to the struct and does not copy the entire struct out like the List<T> indexer. Also, an array is more efficient than List<T>.
If you need to grow the array over time, then create a generic class that works like List<T>, but uses arrays underneath.
There is an alternative solution. Create a class that incorporates the structure and create public methods to call the methods of that structure for the required functionality. Use a List<T> and specify the class for T. The structure may also be returned via a ref returns method or ref property that returns a reference to the structure.
The advantage of this approach is that it can be used with any generic data structure, like Dictionary<TKey, TValue>. When pulling a struct out of a Dictionary<TKey, TValue>, it also copies the struct to a new instance, just like List<T>. I suspect that this is true for all C# generic containers.
Code example:
public struct Mutable
{
private int _x;
public Mutable(int x)
{
_x = x;
}
public int X => _x; // Property
public void IncrementX() { _x++; }
}
public class MutClass
{
public Mutable Mut;
//
public MutClass()
{
Mut = new Mutable(2);
}
public MutClass(int x)
{
Mut = new Mutable(x);
}
public ref Mutable MutRef => ref Mut; // Property
public ref Mutable GetMutStruct()
{
return ref Mut;
}
}
private static void TestClassList()
{
// This test method shows that a list of a class that holds a struct
// may be used to efficiently obtain the struct by reference.
//
var mcList = new List<MutClass>();
var mClass = new MutClass(1);
mcList.Add(mClass);
ref Mutable mutRef = ref mcList[0].MutRef;
// Increment the x value defined in the struct.
mutRef.IncrementX();
// Now verify that the X values match.
if (mutRef.X != mClass.Mut.X)
Console.Error.WriteLine("TestClassList: Error - the X values do not match.");
else
Console.Error.WriteLine("TestClassList: Success - the X values match!");
}
Output on console window:
TestClassList: Success - the X values match!
For the following line:
ref Mutable mutRef = ref mcList[0].MutRef;
I initially and inadvertently left out the ref after the equal sign. The compiler didn't complain, but it did produce a copy of the struct and the test failed when it ran. After adding the ref, it ran correctly.