A VB.NET Critique

E

Eric

Jon Skeet said:
No, *not* everything in .NET is an object. Not everything in .NET is a
reference type.

I can see where you're coming from, but you're wrong. I believe section
8.2.4 of the ECMA spec is the relevant bit:

The important bit of 8.9.7 is:

<quote>
Value types do not support interface contracts, but their associated
boxed types do.
A value type does not inherit; rather the base type specified in the
class definition defines the base type of the boxed type.
</quote>

A look a the class hierarchy via the object browser in VS.NET,
apparently shows that System.ValueType inherits from System.Object.

Cheers,
Eric :)
 
J

Jon Skeet [C# MVP]

Edward G. Nilges said:
OK, this denies that the value is an object from the standpoint of the
spec. And I agree that you cannot inherit a value type.

Nonetheless, value types Inherit from object.

No, the *boxed* types inherit from object (via ValueType). This *is* an
important distinction.
I think there's a real danger here of a Scholastic and pointless
discussion of what is and is not an object. I'd point out that C
experts CLAIM that their values are objects.

I'd say the hell with it. Who cares. Use the value right and vaya con
dios.

But you are saying that to be an object the type must have a reference
type. Doesn't this mean that it's wrong to speak of value objects? OK,
but then why do values have properties and methods?

I'll agree that it's difficult in terms of terminology, but I think
it's worth being clear on it where possible. The value "5" for instance
(as a bit pattern), has no actual type, even if you know the length -
if it's a 32 bit "5" it could be an int32, a uint32 or a single, as the
says. The value itself is not an object - but methods can be called on
the boxed type which act on a value *as if* the value is an object.
I think an overly Scholastic reading would make for poor pedagogy.

Whereas I think that an overly "loose" reading where everything's a
reference type when it clearly isn't makes for poor understanding and
communication.
 
J

Jon Skeet [C# MVP]

Eric said:
Implementation wise, yes, but conceptually, no. See response to Programmer Dude.

No, conceptually not everything is an object either. Please read the
quotes from the spec *very carefully* - preferrably reading them in the
context of the rest of the spec as well.
 
J

Jon Skeet [C# MVP]

Eric said:
A look a the class hierarchy via the object browser in VS.NET,
apparently shows that System.ValueType inherits from System.Object.

Please read the quote again, carefully. The type System.Int32, for
instance, isn't the actual type of an integer - it's the *boxed* type
of an integer.

Can you not see how what you're saying is going against what the spec
says?
 
J

Jon Skeet [C# MVP]

Eric said:
That is what I believe Daniel is getting at in his response when he
says native types are not objects. From an implementation point of
view, he is correct and native types (or "value types" in .NET
parlance) are not reference types and dont behave like objects. But
*conceptually*, if you apply the definition of inheritance in OOP an
Integer for instance, is a reference type:

If my logic can be proved unsound, then I will stand corrected.

Your logic is unsound because although the *boxed* type for Integer
does indeed inherit from object, the value type itself doesn't, and
can't. As the specification says, value types themselves *do not*
inherit - only their boxed types do.

Now, whether "System.Int32" (Integer) is the boxed type or the value
type itself seems a bit confused, and seems to depend on context. This,
I'll readily agree, is a pain when trying to be precise about these
things.
The specification states that the class definition is used to define
both of them.

The specification *is* very clear, however, that not everything is an
object type, or a reference type. It even says explicitly "Value types
are not object types".
 
E

Eric

Jon Skeet said:
It makes perfect sense. The first one has defined a variable of type
"reference to array of integers", and has implicitly set the value to
nothing.

The second one has defined a variable of type "reference to array of
integers", and has explicitly set the value to be a reference to an
empty array.

The reason I personally think its not logical...hmm, actually it is
logical, its just not friendly so to speak. Simply defining the
default value of an array to be a zero length array, such that:

Dim arr1() As Integer

is synonymous with:

Dim arr1() As Integer = {}

would, IMO, be more practical.

Arrays are reference types in all respects - how are they "half way" to
being value types?

For instance, you are not required to use the New operator and can do:

Dim arr() As Integer = {1, 2, 3}

instead of:

Dim arr() As Integer = New Integer() {1, 2, 3}

This absence of New in the first syntax gives no indication that
arrays are indeed reference types and are allocated on the heap. It
makes it appear as if arrays are stack allocated and are hence value
types. A similar thing can be said about the New operator and
structures.

My reasoning for this is based on my understanding of the semantics of
the New operator both from VB6 and other OOP languages like C++ and
Java, but YMMV.

(Note: that syntactical allowance is done for convenience and I am
glad its allowed. For other syntactical exceptions that have to be
done to make type unification viable, see concepts such as "manifest
classes" in section 3.3 of the Blue language paper at
http://tinyurl.com/yryyz )
Yes, but if arrays weren't objects there's be any number of other
problems with them. Passing them around would be a nightmare, for
instance - you'd have to pass them by reference all over the place just
for the sake of performance. Yuk.

I agree but there is a way to implement them to satisfy both the
performance criteria and the avoidance of NullReferenceException.
There are two parts to an array. The descriptor, which contains type
information on the array (number of dimensions, size of each
dimension, pointer to data, etc) and the actual array data. If you
allocate the descriptor on the stack and the data on the heap the
problem vanishes. One can check the array type info without getting
exceptions because the descriptor is stack allocated, and passing them
around can be defined as copying the descriptor rather than the data
hence removing the performance penalty.

Furthermore, since ReDim can't change the number of dimensions for an
array in VB.NET, the above proposal of stack allocating the descriptor
works well. In VB6, the descriptor would need to be allocated on the
heap (due to ReDim's behaviour) otherwise the compiler writer will
need some serious black magic to implement dynamic allocation within
the current procedure's stack frame.
Unless there is an array, there *is* no upper bound though - and an
array always has all its possible elements, even if some of them are
null/nothing. In your previous example, arr1.Length wasn't saying "what
is the length of the array which is arr1's value", it's saying "what is
the length of the array which arr1's value refers to" - and given that
arr1's value *doesn't* refer to an array, it's natural that there
should be an exception.

Natural? Yes. Convenient and efficient? Not IMO. (See previous
comments in this same post).
It's important to distinguish between the idea of the value of a
variable being an actual array (which it isn't) and it being either a
reference to an array, or nothing.

Agreed. One thing though: it can be quite convenient if the variable
is the actual array (i.e the array descriptor and data are stack
allocated) and the performance gains for small, fixed-size arrays
(such as the 3x3 transformation matrices used in graphics programming)
can be very substantial.
You shouldn't usually be really *worrying* about
NullReferenceExceptions though - either null is a valid value for the
variable at that point in time, in which case it may well have a
special meaning as distinct from an empty array, or it may not be
valid, in which case throwing an exception is perfectly reasonable
(although you may wish to make the check at the start of the method if
it's coming in as a parameter, for instance).

You are right that I shouldn't have to be worrying about
NullReferenceExceptions but with the current implementation, I have
no choice but to worry. Even if I do check incoming parameters,
VB.NET doesn't allow declarations of fixed-size arrays and I can wipe
out the array at any time and later get into trouble:

Sub Bar(ByVal arr() As Integer)
If arr Is Nothing Then
'take appropriate action
End If

'some code here

Erase arr

'some more code here

For n As Integer = 0 To arr.Length - 1
'blah
Next
End Sub

The For loop fails with a NullReferenceException because of using the
Length property on a null object. To avoid that, I either have to
ensure that an array is never wiped out (an endeavour that will fail
atleast once) or litter my code with "If" statements to check for the
possibility each time I want to access an instance member of the Array
class.
Given the above (and my previous post), I'm afraid I have to disagree
with you. The suggestion that all types are reference types is the
biggest problem, IMO.

I should have been clearer initially, about the conceptual and
implementation aspects when giving my explanation. My bad.

Cheers,
Eric :)
 
J

Jon Skeet [C# MVP]

Eric said:
The reason I personally think its not logical...hmm, actually it is
logical, its just not friendly so to speak. Simply defining the
default value of an array to be a zero length array, such that:

Dim arr1() As Integer

is synonymous with:

Dim arr1() As Integer = {}

would, IMO, be more practical.

What would

Dim arr1() As Integer = Nothing

do then? If it would create a variable with an initial null reference
value, then you still haven't bought much as array type expressions can
still be null and you can still get NullReferenceExceptions. Indeed,
you'll have that problem anyway as other languages *do* allow array
type expressions to be null.

When is it actually practical to use an array type variable without
assigning a reasonable value to it anyway? I would argue that actually
using the value of arr1 when it's only been declared as

Dim arr1() As Integer

and hasn't had an explicit value assigned to it yet is almost always
going to be incorrect program logic anyway.
For instance, you are not required to use the New operator and can do:

Dim arr() As Integer = {1, 2, 3}

instead of:

Dim arr() As Integer = New Integer() {1, 2, 3}

This absence of New in the first syntax gives no indication that
arrays are indeed reference types and are allocated on the heap. It
makes it appear as if arrays are stack allocated and are hence value
types. A similar thing can be said about the New operator and
structures.

Ah, right. Yes, the language here is hiding the fact that it's a
reference type to some extent - but it still *is* a reference type,
entirely.
My reasoning for this is based on my understanding of the semantics of
the New operator both from VB6 and other OOP languages like C++ and
Java, but YMMV.

Array types are reference types in Java as well, but the following is
perfectly legal:

public class Test
{
public static void main(String[] args)
{
String[] test = {"hello", "there"};
for (int i=0; i < test.length; i++)
{
System.out.println (test);
}
}
}

Again, no new but the array itself is on the heap. It's just a common
syntactical allowance.
(Note: that syntactical allowance is done for convenience and I am
glad its allowed. For other syntactical exceptions that have to be
done to make type unification viable, see concepts such as "manifest
classes" in section 3.3 of the Blue language paper at
http://tinyurl.com/yryyz )

Right - but it *is* only a syntactical allowance, and doesn't in any
way show the type system *itself* to have "half-way" types.
I agree but there is a way to implement them to satisfy both the
performance criteria and the avoidance of NullReferenceException.
There are two parts to an array. The descriptor, which contains type
information on the array (number of dimensions, size of each
dimension, pointer to data, etc) and the actual array data. If you
allocate the descriptor on the stack and the data on the heap the
problem vanishes. One can check the array type info without getting
exceptions because the descriptor is stack allocated, and passing them
around can be defined as copying the descriptor rather than the data
hence removing the performance penalty.

Interesting idea, although I'll need to think about it further before
necessarily agreeing it would be a *good* idea. Of course, you could
implement that yourself quite easily as a value type which contained an
array. Until generics come in, you'll have to create a different type
for each array type you want, of course.

I'll think about that some more. I *think* I actually rather like being
able to tell the difference between a 0-length array and a null
reference though.

Also, if array types were value types, people would expect them to
*act* like value types, so you could pass them with impunity and not
have the values inside them change. That wouldn't be true with your
system. Value types which contain mutable reference types always seem a
bad idea to me, just in terms of having misleading semantics. I guess
we could get used to it for arrays in the same way we have with the
different syntax for constructing them.
Natural? Yes. Convenient and efficient? Not IMO. (See previous
comments in this same post).

It's reasonably efficient, I believe, and it *does* have the
convenience of allowing you to distinguish between null and an empty
array.
Agreed. One thing though: it can be quite convenient if the variable
is the actual array (i.e the array descriptor and data are stack
allocated) and the performance gains for small, fixed-size arrays
(such as the 3x3 transformation matrices used in graphics programming)
can be very substantial.

But then you have horrendous problems with large arrays. I think all in
all I'm much happier with them being reference types.
You are right that I shouldn't have to be worrying about
NullReferenceExceptions but with the current implementation, I have
no choice but to worry. Even if I do check incoming parameters,
VB.NET doesn't allow declarations of fixed-size arrays and I can wipe
out the array at any time and later get into trouble:

Sub Bar(ByVal arr() As Integer)
If arr Is Nothing Then
'take appropriate action
End If

'some code here

Erase arr

'some more code here

For n As Integer = 0 To arr.Length - 1
'blah
Next
End Sub

The For loop fails with a NullReferenceException because of using the
Length property on a null object.

Absolutely - and you want it to, IMO, as it's showing a bug in your
code.
To avoid that, I either have to
ensure that an array is never wiped out (an endeavour that will fail
atleast once)

So you'll get an exception to show that you've got a bug in your code.
That's what exceptions are for. This is better than the language
masking the fact that you've made a mistake and just silently running
an empty loop effectively.
or litter my code with "If" statements to check for the
possibility each time I want to access an instance member of the Array
class.

No, you don't want to do that unless you know that null/Nothing is a
valid value for the variable at that time. If it is, you need to be
checking it anyway. If it's not, you should get an exception so you can
find out where the problem is. Otherwise it would be like FileStream
not failing if you ask it to open a file which doesn't exist, and
instead returning an empty stream - it masks errors rather than helping
you to fix them.
I should have been clearer initially, about the conceptual and
implementation aspects when giving my explanation. My bad.

I'm afraid I still think you're wrong - the spec clearly states that
not all types *are* reference types, conceptually as well as in
implementation.
 
E

Eric

David <[email protected]> wrote in message news:<[email protected]>...

I will quote myself from another group, to show my reasoning behind
that argument:

<quote start>

This is going to be an interesting problem area of the .NET CTS
resulting from the difficult (and in some ways, pointless) goal of
type unification. Everything in .NET (and hence VB7) *is* an object
because all types either directly or indirectly inherit from Object.
If you agree with me so far, then you will also agree that *all* types
in .NET are actually *reference* types its just that *some* of those
reference types (those that inherit from ValueType) have *value
semantics*. Now then, the Is operator checks if two variables refer to
the same object. By way of logical deduction, the Is operator should
work in all cases because it works on object types and every type in
.NET is an object. The problem? It doesn't. While it is easy to accept
that and just work with it, the issue is still a consistency problem
from a language design point of view and is of more relevance to those
interested in language design. Such exceptions have to be made all the
time when one attempts type unification. Other examples abound in .NET
but to see this in a different language look at this article on the
Blue language (especially section 3.3): http://tinyurl.com/yryyz

<quote end>

Realising the number of languages on .NET and the different wording in
the specs for each of those languages, I would like to reduce the
scope of my statement given above:

"...Everything in .NET (and hence VB7) *is* an object because all
types either directly or indirectly inherit from Object..."

and narrow it down to VB.NET rather than the whole of .NET. It may
still apply to the other languages, but I am not going to gamble on
that by making a potentially wrong all-encompasing statement.

<snip lots more>


Cheers,
Eric :)
 
E

Edward G. Nilges

Jon Skeet said:
No, the *boxed* types inherit from object (via ValueType). This *is* an
important distinction.

OK, I accept it's an important distinction at run time. But my concern
is describing source, where it's meaningful to point out that a value
type can be, notationally an object.
I'll agree that it's difficult in terms of terminology, but I think
it's worth being clear on it where possible. The value "5" for instance
(as a bit pattern), has no actual type, even if you know the length -
if it's a 32 bit "5" it could be an int32, a uint32 or a single, as the
says. The value itself is not an object - but methods can be called on
the boxed type which act on a value *as if* the value is an object.

You have made an important point. The value is a pure value.
Whereas I think that an overly "loose" reading where everything's a
reference type when it clearly isn't makes for poor understanding and
communication.

I agree.

OK, can we agree that virtually a value type is an object?
 
E

Eric

Jon Skeet said:
<snip>

Actually, *not* everything is an object. Everything can be *converted*
to an object via boxing, but a value type value itself is *not* an
object.

You are correct. A value *type* (like Integer) is an Object by way of
inheritance, but a value type *value* (like "5") is itself not an
object or if it is, I (like you) wouldn't refer to it as such.

When I quoted myself earlier on in the thread, I used the
all-encompasing word "everything" which would include both values and
types. That didn't quite express what I had in mind, and that was:
every *type* is an object.

I have a bad feeling we might end up having to face the challenge of
coming up with universally-agreed-upon definitions of such terms as
"value", "object", "class" and "type" and more importantly, the
distinctions between those definitions both in natural languages and
in programming languages.

Cheers,
Eric :)
 
C

Cor

Hi Eric,

What I did show with the date was only meant as an example,

By saying UK time, while I thought the time notation that the UK uses is
used in most parts of the world where arabic figures are used and certainly
in Europe, while the US time notation is more rare, you give something extra
that is not needed for your investigation.

When you just tell about the different notations is enough in my opinion.

That is only an example I used because it was the most simple to show that
on some places you try to prove to much and you come in unsure area's, while
for those who should read your page, it has to be clear.

In my opinion is with your option of giving a date format is nothing wrong,
however exact as you describe the option is clear enough.

An other question, why you have included more newsgroups than VB.net
language, the arguing you had with Jon was already done a short time ago by
OHM, Armin and Jay B., and I thought that at the end of that thread all was
clear.

And I thought that I myself had send a sample to this newsgroup about the IS
and the = operators in this kind of situations.

What I mis in your page is the discussion we had in this newsgroup about
If Not object isNot Nothing
If object Is Nothing
if object Not Is Nothing
If object IsNot Nothing
and the in the eyes from Jay B and me better alternative
Is object Is Something
Is Not object Is Something

Personaly I find that more important than a boxed situation, in which as far
as I could see the VB.net language is consequent (If you want my sample I
can to try to find it, but I post a lot you know and it was in a question I
do not know anymore)

I hope this helps?

Cor
 
J

Jon Skeet [C# MVP]

Edward G. Nilges said:
OK, I accept it's an important distinction at run time. But my concern
is describing source, where it's meaningful to point out that a value
type can be, notationally an object.

OK, can we agree that virtually a value type is an object?

I'm not sure I'd *quite* go with either of those (mostly because I
don't know what *exactly* you mean by "virtually" in the second one).
How about that a value type can be transparently viewed as an object? I
don't think it matters whether or not we agree exactly at this stage
though - we clearly understand the same things, fundamentally.
 
J

Jon Skeet [C# MVP]

Eric said:
You are correct. A value *type* (like Integer) is an Object by way of
inheritance, but a value type *value* (like "5") is itself not an
object or if it is, I (like you) wouldn't refer to it as such.

Well, all types are themselves objects, and instances of the Type
class, but I don't *think* that's what you're talking about. Whether or
not Integer *derives* from Object (indirectly) depends on which
"Integer" type you're talking about. There's the "boxed type for
Integer" and the "value type for Integer". Both are defined by the same
class definition, and the appropriate type is used at runtime depending
on the situation. For instance, whenever a method is called on a type,
the boxed type is used.
When I quoted myself earlier on in the thread, I used the
all-encompasing word "everything" which would include both values and
types. That didn't quite express what I had in mind, and that was:
every *type* is an object.

I have a bad feeling we might end up having to face the challenge of
coming up with universally-agreed-upon definitions of such terms as
"value", "object", "class" and "type" and more importantly, the
distinctions between those definitions both in natural languages and
in programming languages.

I've been at least *attempting* to use those terms solely according to
the ECMA spec. I would *hope* those definitions also apply to VB.NET,
but I wouldn't like to say for sure.
 
J

Jon Skeet [C# MVP]

Eric said:
Realising the number of languages on .NET and the different wording in
the specs for each of those languages, I would like to reduce the
scope of my statement given above:

"...Everything in .NET (and hence VB7) *is* an object because all
types either directly or indirectly inherit from Object..."

and narrow it down to VB.NET rather than the whole of .NET. It may
still apply to the other languages, but I am not going to gamble on
that by making a potentially wrong all-encompasing statement.

I wouldn't like to even say that, myself. I'm pretty sure that VB.NET
also talks about boxing - and the *only* reason that boxing is even
needed is to convert a "pure value" into an object because it isn't one
to start with, surely? However, I'm not particularly bothered by
pushing the point :)
 
C

CBFalconer

Cor said:
What I did show with the date was only meant as an example,

By saying UK time, while I thought the time notation that the UK
uses is used in most parts of the world where arabic figures are
used and certainly in Europe, while the US time notation is more
rare, you give something extra that is not needed for your
investigation.

While I consider anything with VB or .NET in it anathema, there
should still be no reason for date confusion today. There has
been an ISO standard for date and time format for many years.
Today is 2004-01-10, for example, with no ambiguities. The
(local) time is 07:13:30. In GMT we are at "2004-01-10 12:13:30".
 
C

Cor

Hi CBFalconer

I find it a very good point and a pitty that it is not everywhere
implemented as that.

I think that the date implementation is so strange because some todays
languages are often started as learning tools in the USA or just as an USA
oriented scripting tool (The most worse in that in my eyes SQL).

However I find too it would be a giant step to better if that ISO standard
was default everywhere.

Cor
 
E

Edward G. Nilges

Jon Skeet said:
I'm not sure I'd *quite* go with either of those (mostly because I
don't know what *exactly* you mean by "virtually" in the second one).
How about that a value type can be transparently viewed as an object? I
don't think it matters whether or not we agree exactly at this stage
though - we clearly understand the same things, fundamentally.

My only remaining question is what to tell training classes consisting
of Visual Basic 6 programmers, armed as usual with pitchforks and
torches and angry and upset about having to change, whether
"everything is an object".
 
J

Jon Skeet [C# MVP]

Edward G. Nilges said:
My only remaining question is what to tell training classes consisting
of Visual Basic 6 programmers, armed as usual with pitchforks and
torches and angry and upset about having to change, whether
"everything is an object".

I'd tell them the truth. The earlier people get into the idea of
references, reference types, values, value types and boxing, the better
IMO. It affects just about everything else you do.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top