[.net] More C# growing pains, structs/classes and references

Started by
5 comments, last by sjelkjd 17 years, 11 months ago
Ahh, I love learning yet another new language. And now I've just learned the truth behind references and how they relate to structs/classes in C#. If I have a list of structs, when I access the list by index I get a return by value, and not by reference. If I need to access these structs by reference, is there anything I can do? I know I can write a class that contains the struct, but is there a way to specify that a List contain references to structs rather than values? What I have is something like this: (One more complication is that the object stored in the list is unknown)

class holdsManyThings<T>
{
  public List<T> = new List<T>();
}
But since T is sometimes a struct I'd really like to do something like this:

class holdsManyThings<T>
{
  public List<ref T> = new List<ref T>();
}
which I realize doesn't make much sense syntax wise, but I think you know what I mean. Is the only answer to encapsulate my struct in a class object?
Advertisement
Care to be more specific about what you're actually trying to do? Why are the structs structs in the first place if you need to access them as a reference?
The structs where not written by me, and I can't really chane them. They store vectors/matrices etc... and I just want a list of them, that I can edit like so:

holdsManyThings.myList[0].X = 25

or something like that. If the value in the list is a struct this results in compile errors (much to my surprise, it took me quite some time to even realize that the problem was relating to structs.)
You can't do it. You have to do something like this instead:

Vector foo = holdsManyThings.myList[0];
foo.X = 25;
holdsManyThings.myList[0] = foo;
The only practical way around it is to implement a class type wrapping around the original struct. Or just change the struct to a class.
Rob Loach [Website] [Projects] [Contact]
Thanks, I'm rather dissapointed in some of the C# design decisions. It's strange to me that the person defining the object/struct is also deciding how the object is passed in.

Oh well, react, adapt and move on.
Expanding on your comment a bit, in c# when you declare a new type, you decide whether it is a reference type(class), or a value type(struct). These declarations govern usage semantics.

Classes: are passed by reference. .Equals and == will compare references for equality(unless overriden).

Structs: are passed by value. .Equals and == will do a memberwire comparison to test for equality.

However, you can pass both structs and classes by reference, using the ref keyword. This does the obvious thing for a struct (For a class, it passes the pointer by reference). However, you are limited to passing method arguments by reference - you cannot pass return types by reference. So it is impossible to make a getter return a value by reference, which is what you are looking for.

Now, let us consider the ramifications of allowing pass-by-reference of value types. C++ lets you do something that is quite evil:
int& foo(){  int local = 5;  return local;}

When foo exits, a reference to local is still around - even though the stack for local has been cleaned up! Let's try a different tack: allowing the programmer to declare a value type to be instantiated as a reference type.
ref int foo(){  ref int local = 5;  return local;}


There are some things that fall out of such a solution. One, you would have to allow conversions between ref-declared value types and non-ref-declared value types. This is murky, because you would like to preserve references whenever possible, as in the following example:
class Point{  private float x, y;  public ref float X { get { return this.x; } }  public ref float Y { get { return this.y; } }}

We surely don't want to have to declare x and y as ref in our class(since they would be allocated external to the Point class). So we have to allow a conversion from a value-type field to a ref-value type. But what if Point is declared as a struct? Now, the previously mentioned conversion is illegal, since x and y would be declared on the stack and could fall out of scope. You could enforce a pointer check on any ref-value type before derefing it, but how can you ensure that it is still valid?

Notice the huge number of special cases and exceptions given the proposed feature. Also, consider that, for the most part, you will treat reference types as reference types, and value types as value types. So should C# make the type declaration rules more complex to support a small set of cases? The language designers said no, and I think they made the right choice.

Also, read what the .NET design guidelines have to say.
Quote:
DO NOT define a struct unless the type has all of the following characteristics:

It logically represents a single value, similar to primitive types (int, double, etc.).
It has an instance size under 16 bytes.
It is immutable.
It will not have to be boxed frequently.
In all other cases, you should define your types as classes.

The problems you are having with value types go away when using immutable value types. Now, it's probably the right decision to make your point/vector class a struct, but now you know why it's such a pain to change them when they are exposed as properties.

Finally, consider the following code:
List<Point> myPoints; ...// set a pointmyPoints[0] = new Point(25, 0);// add something to a pointmyPoints[1] += new Vector(10, 0);

Was that so hard? ;)

This topic is closed to new replies.

Advertisement