I realize that structs are meant to be immutable, that mutating structs is evil, and the proper way to change values in a struct is by creating new instances. However, I am not clear on the memory and performance aspects/issues of new instances versus allowing the struct to be mutable.
Suppose I have the struct,
struct Vehicle
{
public readonly int Number;
public readonly double Speed, Direction;
public Vehicle(int number, double speed, double direction)
{
this.Number = number;
this.Speed = speed;
this.Direction = direction;
}
}
And create it as:
Vehicle car;
as well as
Vehicle plane = new Vehicle(1234, 145.70, 73.20)
If I need to assign to Number, Speed, or Direction later, I could remove the readonly and make the struct mutable – I know it is eveil to do this. – thereby “mutating” an already created struct value.
Alternatively I could create a new struct instance. So instead of saying car.Speed = 120.7; I could say car = new Vehicle(car.Number, 178.55, car.Direction);. That would create a new struct value almost like the old value, only with the Speed changed. But it wouldn’t mutate the existing struct value.
Here is the issue. Suppose, as an extreme example, I need to update the speed and/or the direction many, many thousands of times per second. I would think that the creation of this many instances would severely impact memory and performance, and that in this case it would be better to allow the struct to be mutable.
Can anyone please clarify the memory and performance issues of a mutable struct versus the proper way to implement a struct in this type of an extreme case?
There are two distinct usage cases for structs; in some cases, one wants a type that encapsulates a single value and behaves mostly like a class, but with better performance and a non-null default value. In such cases, one should use what I would call an opaque structure; MSDN’s guidelines for structures are written on the assumption that this is the only usage case.
In other cases, however, the purpose of the struct is simply to bind some variables together with duct tape. In those cases, one should use a transparent struct which simply exposes those variables as public fields. There is nothing evil about such types. What’s evil is the notion that everything should behave like a class object, or that everything should be “encapsulated”. If the semantics of the struct are such that:
and any change to them would break the code which uses it, then there is nothing which future versions of the struct could possibly do which a transparent struct would not be able to do, nor is there anything that a transparent struct would allow which a future version of the struct would be able to prevent. Consequently, encapsulation imposes cost without adding value.
I would suggest that one should endeavor to, whenever practical, make all structs either transparent or opaque. Further, I would suggest that because of deficiencies in the way .net handles struct methods, one should avoid having opaque structures’ public members modify
thisexcept perhaps in property setters. It is ironic that while MSDN’s guidelines suggest one shouldn’t use a struct for things that don’t represent a “single value”, in the common scenario where one simply wants to pass a group of variables from one piece of code to another, transparent structs are vastly superior to opaque structs or class types, and the margin of superiority grows with the number of fields.BTW, with regard to the original question, I would suggest that it’s useful to represent that your program may want to deal with two kinds of things: (1) a car, and (2) information related to a particular car. I would suggest that it may be helpful to have a struct
CarState, and have instances ofCarhold a field of typeCarState. This would allow instances ofCarto expose their state to outside code and possibly allow outside code to modify their state under controlled circumstances (using methods likeNote that such methods offer most of the performance advantages of having the car’s state be a mutable class, but without the dangers associated with promiscuous object references. A
Carcan let outside code may update a car’s state via the above methods without allowing outside code to modify its state at any other time.BTW, I really wish .net had a way of specifying that a “safe” struct or class should be considered as encapsulating the members of one or more of its constituents [e.g. so that struct which held a
RectanglecalledRand anStringcalledNamecould be regarded as having a fieldsX,Y,Width, andHeightwhich alias the corresponding struct fields. If that were possible, it would greatly facilitate situations where a type needs to hold more state than previously expected. I don’t think the present CIL allows for such aliasing in a safe type, but there’s no conceptual reason it couldn’t.