Sign in to follow this  
Ataru

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

Recommended Posts

Ataru    133
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?

Share this post


Link to post
Share on other sites
Telastyn    3777
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?

Share this post


Link to post
Share on other sites
Ataru    133
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.)

Share this post


Link to post
Share on other sites
sjelkjd    171
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;

Share this post


Link to post
Share on other sites
Rob Loach    1504
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.

Share this post


Link to post
Share on other sites
Ataru    133
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.

Share this post


Link to post
Share on other sites
sjelkjd    171
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 point
myPoints[0] = new Point(25, 0);

// add something to a point
myPoints[1] += new Vector(10, 0);



Was that so hard? ;)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this