.NET Forum / .NET Framework / New Users / August 2005
How come value types are not true objects?
|
|
Thread rating:  |
Bob Bryan - 07 Aug 2005 09:52 GMT In C++, it is not uncommon to design a class to hold a pointer to another object. For example:
class foo { Object * Obj; void SetObj(Object * obj) { Obj = obj; } Object * GetObj() { return Obj; } };
The nice thing about this simple class is that once the pointer is set with SetObj, it does not matter how, when, or where Obj is modified. Class foo will be able to see the most recent updates to the object because it is not using a copy, but rather a reference to the actual object that is being used by other classes.
Under the .NET framework, classes or reference types also work the same way. For example:
class foo { Object Obj; void SetObj(Object obj) { Obj = obj; } Object GetObj() { return Obj; } }
This C# code is very similar to the C++ code above and works the same way as the C++ code. However, consider the following code:
class foo { int Num; void SetNum(int num) { Num = num; } int GetObj() { return Num; } }
The above code works quite a bit differently than the prior 2 examples. Num is a native value type that is passed by value. So, a copy of the contents of num is assigned to the class variable Num. If the integer passed to SetNum is modified by another class outside of foo, then foo won't know about it. Even if num were passed by reference, I don't believe there is any legitimate safe mode C# syntax that will assign the pointer to the class variable Num. So, it seems that reference types and value types have been designed to operate quite differently in .NET. Why is that? I understand that Java and other modern languages don't seem to have this problem.
As a developer, this is quite inconvenient. I am currently working on a C# project where I have had to create classes like RefInt, RefDbl, and RefString to simply hold the appropriate value type (with no other functionality) so that my classes can be used without having to pass the value types each time a method from my class is called. For example, here is some current code from one of my projects:
public class RefInt { // This class exists as a work around to the fact that the .NET framework does not allow // native data types to be used as true objects, or for the address of a native data type to // be assigned like a regular reference type.
public int I; // The value to be used.
public override string ToString() { return I.ToString(); } }
I am faced with the unhappy prospect of telling the users of my classes to either always use the RefInt class variable directly in their code, or to keep track of when they update their local variables that are being used with my class and to move it into the RefInt variable before calling any of my class methods.
The class that I'm writing encapsulates the functionality of a report file with columns. The user of my class calls an add method to add columns. The column data can be used with an int, double, or string and the add method assigns the variable to be used to the class. So, the only options I see are to either have the user always use one of my Ref wrapper classes (like RefInt) in all their calculations, or tell them to move the local variable into my wrapper class before calling my class methods. If anyone can suggest some other way of doing it, I would love to hear it. But for right now, these are the only 2 solutions that I see. Also, even if someone does think of a better way to do this, the point about value types acting differently than reference types is still of concern.
So, it seems to me that anytime the language forces you to do something unpleasant like this, its bad for all concerned. Bad for my customers, bad for me, and eventually its bad for Microsoft since other languages like Java don't have this limitation. So, I am requesting Microsoft to remove this limitation and make C# as well as all the other .NET languages truly object oriented by making value types act like references.
How to do this with minimal impact? One way might be expand the native data types such that for each value type a new reference-value type is added. The reference-value type would have the same properties as a value type, except that it would act as a reference and be allocated on the heap and subject to garbage collection like other reference types. Value types would still work the same way in this scenario. A way of assigning the address of a reference-value type would have to be provided. The = operator should still assign the contents of one variable to another in order to provide backward compatibility. Perhaps := could be used to assign an address. Value types could not have their address assigned and a reference-value could not be assigned to anything other than another referernce-value variable of the same type. This approach should be backwards compatible with all existing code. Another simpler approach would be to simply create all variables on the heap and thus subject them to garbage collection. If this were done, then there would not be a need for a new set of reference-value variables. But, most programs would run a little slower and the := assignment statement would still be needed to assign the address.
I tried to submit this as a suggestion in the Microsoft MSDN product feedback, but the system would not let me get past the search for suggestions. So I a submitting this here. Could someone from Microsoft please refer this suggestion to the appropriate team.
Thanks,
Bob Bryan
Lloyd Dupont - 07 Aug 2005 10:30 GMT Answers inline
> class foo > { [quoted text clipped - 12 lines] > designed to operate quite differently in .NET. Why is that? I understand > that Java and other modern languages don't seem to have this problem. Java work the same, it's even worse! while it is legal in C# to write object o = 42; it's illegal in Java. what other modern language were you speaking of?
> As a developer, this is quite inconvenient. I am currently working on a C# > project where I have had to create classes like RefInt, RefDbl, and RefString [quoted text clipped - 18 lines] > } > } I can't quite see what's the purpose of your class, anyway, with 2.0 you could solve that easily
class Ref<T> where T: struct { public T Value; public override string ToString() { return Value .ToString(); } }
> I am faced with the unhappy prospect of telling the users of my classes to > either always use the RefInt class variable directly in their code, or to > keep track of when they update their local variables that are being used with > my class and to move it into the RefInt variable before calling any of my > class methods. I'm not sure to understand your problem but I will hazard that event is what you need to solve it.
> The class that I'm writing encapsulates the functionality of a report file > with columns. The user of my class calls an add method to add columns. The [quoted text clipped - 7 lines] > of a better way to do this, the point about value types acting differently > than reference types is still of concern. what about the user keeping a reference to the column and call the column's method aColumn.SetData(aData); to update the value?
> So, it seems to me that anytime the language forces you to do something > unpleasant like this, its bad for all concerned. Bad for my customers, bad > for me, and eventually its bad for Microsoft since other languages like Java > don't have this limitation. I would hazard your class suffer from irrealistic expectation. Many library work around this kind problem without having to change the whole language.
> So, I am requesting Microsoft to remove this > limitation and make C# as well as all the other .NET languages truly object > oriented by making value types act like references. no less!! ;-)
Anyway pointer are banned from C# (in most case) as they prevent garbage collection. So it's the end of your suggestion, I believe...
Jon Skeet - 12 Aug 2005 07:31 GMT On 7/8/05 10:30 am, in article uQgVvKzmFHA.3936@TK2MSFTNGP10.phx.gbl, "Lloyd Dupont" <ld@NewsAccount.galador.net> wrote:
<snip>
> Java work the same, it's even worse! > while it is legal in C# to write > object o = 42; > it's illegal in Java. > what other modern language were you speaking of? Just one slight correction - while Java does indeed work the same, the above *does* work in Java (having changed object to Object) - as of Java 1.5/5.0. It didn't before then.
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet If replying to the group, please do not mail me too
Lloyd Dupont - 12 Aug 2005 09:54 GMT >> Java work the same, it's even worse! >> while it is legal in C# to write [quoted text clipped - 7 lines] > 1.5/5.0. > It didn't before then. wouah, cool! thanks John!
PS: Anyway I think it's too late for me, I've been infected by the .NET bug.... May I R.I.P., uh Program I.P.
Klaus H. Probst - 07 Aug 2005 11:08 GMT You're trying to fit a round peg into a square hole. There's a reason why they're called "reference types" and "value types". It's not a design mistake or oversight, it's how the thing is supposed to work.
If you want to pass a pointer to a primitive type between 16 different class instances (or even worse, expose value pointers directly from class member variables as in your example), that's fine. Use C++. If you want to do it in C#, wrap the value type in a reference type or simply box it. The "inconvenience" of having to do this is one of the tradeoffs of using a managed, GC'ed language like C#.
Pointers are probably the number one reason people write unstable, insecure code in low-level languages. One of the framework's primary objectives is to solve those problems, but you have to live within the confines of the sandbox.
 Signature Klaus H. Probst, MVP http://www.simulplex.net/
> In C++, it is not uncommon to design a class to hold a pointer to another > object. For example: [quoted text clipped - 116 lines] > > Bob Bryan Lloyd Dupont - 07 Aug 2005 11:23 GMT > Klaus H. Probst, MVP > http://www.simulplex.net/ This Simuplex stuff is simply amazing ;-) hehehe...
Bob Bryan - 07 Aug 2005 11:51 GMT > You're trying to fit a round peg into a square hole. There's a reason why > they're called "reference types" and "value types". It's not a design > mistake or oversight, it's how the thing is supposed to work. And I'm saying that it would work better if value reference types could be added to the language... If you can't see that then perhaps you need to think more about the example I gave. Again - why should value types be treated any differently than reference types from an OO point of view. Why should the developer be forced to kluge his way around a problem that should not exist in the first place? It may have been designed this way, but its not a good design. Sue me for saying that the emperor is not wearing clothes if you want... Try asking 10 OO experts (not associated with Microsoft) about this and listen to what they tell you.
> If you want to pass a pointer to a primitive type between 16 different class > instances (or even worse, expose value pointers directly from class member > variables as in your example), that's fine. Use C++. If you want to do it in > C#, wrap the value type in a reference type or simply box it. The > "inconvenience" of having to do this is one of the tradeoffs of using a > managed, GC'ed language like C#. Again, in my code - I do wrap the value type in a class simply so that it can be treated as an object. Do you really think this is the way a modern OO language should work?
> Pointers are probably the number one reason people write unstable, insecure > code in low-level languages. One of the framework's primary objectives is to > solve those problems, but you have to live within the confines of the > sandbox. I agree that pointers can lead to severe bugs. The .NET framework is a great advance over the previous version of Visual Studio 6. Working within the confines of the framework is a given. Then again, are you saying that suggestions for improvements to the framework is a bad idea?
You really don't see how the introduction of reference-value types would help solve having to kluge a class as a workaround simply because there is no other way to get a value type to act as a reference type? Are you aware of the impact that will have on users of my class? Right now, they have to either use my wrapper class for all calculations, or they have to move the value type to my wrapper class each time it gets updated. This is much more painful than you seem to be giving credit for...
Again, Java does not seem to have this problem.
Bob Bryan
> > In C++, it is not uncommon to design a class to hold a pointer to another > > object. For example: [quoted text clipped - 149 lines] > > > > Bob Bryan Lloyd Dupont - 07 Aug 2005 13:22 GMT > Again, Java does not seem to have this problem. Again .. why again? Particularly when this statment is PLAIN wrong! How come you keeps repeating a false statment as a proof of you're right?
I would be curious to see your working java code?
For your information int, double, float, all simple type behave exactly the same in Java & C#. In C# the concpet is just extended to struct (like Point, Rectangle, etc....)
Bob Bryan - 07 Aug 2005 15:30 GMT > > Again, Java does not seem to have this problem. > Again .. why again? [quoted text clipped - 7 lines] > In C# the concpet is just extended to struct (like Point, Rectangle, > etc....) For each native data type, Java has a class that can be used as a reference. For example, look up the difference between int and Integer, or float and Float. Java at least provides a wrapper for each value type so that it can be used as an object.
I've never programmed in Java before, just repeating what others that have worked with Java have told me. I'm sure Java has its own set of unique problems and I'm not about to switch to it over this issue. The point is not really what one language can do over another. Its about making the .NET family languages the best they can be.
I have yet to hear any arguments as to why a reference-value type could not be added to .NET for each current value type. Or, why this was not done in the first place. You don't see the value of being able to treat value types as references? Pretend for a minute that you are using my class. Which would you rather do:
1. Use a native data type like an int and call an AddColumn function that takes this value as a reference. You never have to worry about calling another function to move the current value over to my class. This behavior would work exactly the same way as other .NET reference objects by the way. You would then be free to use an int in your code and would not have to deal with the complexity of any wrapper classes.
2. Use a wrapper class that contains an int and has no other functionality. You would then have to use syntax like the following every time the value changes:
RefInt RHighPrice; // Defined once. int HighPrice; // Defined once
RHighPrice.SetValue(HighPrice); // Called multiple times.
The wrapper class is a workaround to a problem. I'm simply asking for the problem to be fixed. Imagine that there are 20 columns to this report. Thats an extra 20 lines of code that the user would have to supply in order to work with my class. What if they have 20 reports? That's 20 * 20 = 400 lines of extra code. And, what happens if he needs to do this in multiple spots? Can you see why this is a problem now? Why should it have to be more work to use a value type than a reference type?
I know how value types work. I don't know the reasons why it works this way. Someone from Microsoft will have to explain it to me.
Bob Bryan
Lloyd Dupont - 07 Aug 2005 16:10 GMT > For each native data type, Java has a class that can be used as a reference. > For example, look up the difference between int and Integer, or float and > Float. Java at least provides a wrapper for each value type so that it can > be used as an object. oh you mean these one? C# could do the same without the need to declare them. example: in C# you could write: object o = 4;
you cannot in Java and you have to write object o = new Integer(4);
but nothing prevent you from duplicating the Integer class in .NET. and you will need to because Java's Integer are immutable (as String in both languages), so you can hold a reference to it, but you CAN'T modify its value.
> I have yet to hear any arguments as to why a reference-value type could not > be added to .NET for each current value type. As a matter of fact with 2.0 you could do that for all value type with one definition class Ref<T> where T: struct { public Ref() {} public Ref(T t) { Value = t; }
T Value; }
> Or, why this was not done in > the first place. not really needed, again, int is also an object! object o = 4;
> 1. Use a native data type like an int and call an AddColumn function that > takes this value as a reference. You never have to worry about calling > another function to move the current value over to my class. This behavior > would work exactly the same way as other .NET reference objects by the way. > You would then be free to use an int in your code and would not have to deal > with the complexity of any wrapper classes. I see what you mean. For that there is 2 solution: - you could use the suggested template above. - you could do the opposite, hold a reference to the colunm, and modify its data And common, none of these is overkill!!
What about the following : if you don't like value type : just don't use them!
Anyway I found this pattern dangerous. The value could change but no one will know! Better to have event to notify interested person, as in the another generic exemple below class Ref<T> { public Ref() {} public Ref(T t) { val = t; }
T val; T Value { get { return val; } set { if(val == value) return; val = value; if(ValueChanged != null) ValueChanged(this, EventArgs.Empty); } } public event EventHandler ValueChanged; }
> 2. Use a wrapper class that contains an int and has no other functionality. > You would then have to use syntax like the following every time the value [quoted text clipped - 4 lines] > > RHighPrice.SetValue(HighPrice); // Called multiple times. 1st, why so convoluted? Ref<int> ri = new Ref<int>(SomeValue); // when 1st needed ri.Value = SomeOtherValue; // called multiple time
it's as verbose as an int, don't you think?
2nd: after reading you again and again I start to believe you are completely new to C# and need some time to be used to it.
3rd: Anyway I would hardly use such pattern. Anyway let's imagine one day I want to. I won't make such an issue of it and just create my utility class. After all this is a very marginal pattern.
> The wrapper class is a workaround to a problem. I'm simply asking for the > problem to be fixed. Imagine that there are 20 columns to this report. [quoted text clipped - 3 lines] > spots? Can you see why this is a problem now? Why should it have to be more > work to use a value type than a reference type? Something is plain wrong here.. It seems like you want something something to happen when the user change (multiple times) your value. (otherwise why care that much). But, in your model, nothing will ever happend because no-one is ever notified! do you see a problem now?
better hold a reference to the column and do something, like that class Column { public Column(object data) { } object data; public object Data { get { return data; } set { if(data == value) return;
data = value; // do someting, now? } } } Column c = new Column(4); c.Data = 24; // the value change AND something happen c.Data = "Today is a beautifull day"; // the value change AND something happen c.Data = new DateTime(); // the value change AND something happen
> I know how value types work. I don't know the reasons why it works this > way. Someone from Microsoft will have to explain it to me. I explain you: - Value type are much faster. - Just create an Integer class and try to write a for loop with them, compare the perfomance.... - int are stored in register, etc... - they don't need reference counting, they are (most of the time) pass by copy. - using value type is a way to speed up your code a little bit. - they are also never null (sometimes usefull) - in short they are light weight object wrapper around memory zone
C# is almost a "system language" (cf http://channel9.msdn.com/ShowPost.aspx?PostIDh302), so it's why there are structs.
Anyway it's time to go to bed for me. I suggest (kindly, hey! :-) you learn C# before commenting on its future ;-)
Klaus H. Probst - 08 Aug 2005 05:50 GMT > It may have been designed this way, but its > not a good design. Sue me for saying that the emperor is not wearing clothes > if you want... If you want Java, why not use Java?
> Try asking 10 OO experts (not associated with Microsoft) > about this and listen to what they tell you. Thanks, I've had enough years of exposure to OO. And not only in association with Microsoft.
You keep saying "Java does not have this problem". That's because in Java an integer is a reference type, allocated on the heap. That's incredibly wasteful. You have the opinion that C# is badly design because of that - I'd tend to ask what the heck Gosling was smoking when he decided to promote a DWORD to the equivalent a heap-allocated struct with a vtable. The people who designed C# wanted to get away from that and give a set of fast, lightweight native types. If you find them lacking, then do what you were doing to begin with - wrap them. After all, it's not like you're going to take a perf hit from that *over Java* - you'll be writing Java code in C#. More power to you. No one is stopping you from doing that. Or use Ruby. Use Python or Perl. Use OCAML, Squeak, Malbolge, SmallTalk, LISP, etc.
> Then again, are you saying that > suggestions for improvements to the framework is a bad idea? No, you can suggest all you want. I'm just telling you *your suggestion* is a bad idea, in my opinion. We can agree to disagree - and you can always just use Java or some other "real" OO language that forces you to allocate x bytes on the heap (along with the GC overhead) whenever you want to add 1+1. It's not like you don't have choices.
 Signature Klaus H. Probst, MVP http://www.simulplex.net/
Nigel Norris - 08 Aug 2005 09:20 GMT > You keep saying "Java does not have this problem". That's because in Java > an > integer is a reference type, allocated on the heap. Nope - Java has the same model as C#. In the Java they are referred to as 'primitive types', and are not treated as reference types, just like C#. In Java if you want to have reference to an int, you have to instantiate a wrapper object yourself. C# does it auotomatically with boxing.
----- Nigel Norris
Larry - 07 Aug 2005 17:21 GMT > Pointers are probably the number one reason people write unstable, > insecure > code in low-level languages. Pointers are just one aspect of it. Once you know what you're doing though, pointer mistakes are extremely rare. The real problem is achieving a state where you "know what you're doing". Most programmers never do, and pointers are just the tip of the iceberg.
Nigel Norris - 07 Aug 2005 17:07 GMT Bob,
Java has exactly the same distinction between value types and reference types. While it's possible to have 'pure' OO languages where there is no distinction, the industry seems to have decided that performance and other issues make that approach unsuitable for a general purpose language.
Yes, Java has wrapper classes. They are necessary to solve certain cases that C# handles by other means:
- returning value via method arguments - C# can pass parameters by reference - converting values to and from object typ - C# provides boxing
So any use for the wrappers would be only be for the type of approach you want to adopt - and generally most people don't seem to want to do that. I've never seen anyone need to create a wrapper class. However you can if you want to. Note. however, with the value type/reference type distinction, you always have to create an object instance and assign the value to it, which seems to be part of your complaint.
As for C++, yes you can freely create pointers to value types. However there is no safety in this - if you use a pointer to a value type that was allocated on the stack after the stack frame has gone, you will get a very nasty bug, as anyone who has used C++ extensively will know to their cost. Both C# and Java take the same view of removing this kind of dangerous feature in the language. That's the clear design goal, and I doubt it will change.
To be honest, it sounds like you are used to using a hammer, have been handed a screwdriver, and are wondering why it's not very good for hitting things with. Have another look at your design - there's probably an approach which is more sympathetic to the language, and may well be a better design.
Or take look at the ADO.NET classes for some ideas. I'm not completely clear from your description exactly what you are trying to do, but ADO.NET tends to use and an object array to pass rows of data around. Boxing will allow the assignment of various data types to the elements. And an array is a reference type, so you can hang on to a reference to that, if you want to do it that way.
----- Nigel Norris
> In C++, it is not uncommon to design a class to hold a pointer to another > object. For example: [quoted text clipped - 148 lines] > > Bob Bryan Bob Bryan - 07 Aug 2005 22:00 GMT I'm not going to start a flame war over this. I simply won't dignify any response that suggests that I'm new to the language, don't know what I'm doing, fitting a square peg in a round hole etc...
The issue I've described here is also not new. Others in the newsgroups have also complained about this as well. Try doing a search...
Believe it or not, most psychologogists believe that when you put down someone else, you are subconsciously talking about your own limitations.
Bob Bryan
Nigel Norris - 08 Aug 2005 08:46 GMT Bob,
I presume you post is in response to mine.
If I've given offence, I'm sorry. No offence was intended. However I stand by what I said - I don't think it was an unreasonable response to the words you wrote.
Nigel Norris
> I'm not going to start a flame war over this. I simply won't dignify any > response that suggests that I'm new to the language, don't know what I'm [quoted text clipped - 7 lines] > > Bob Bryan
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|