Types system in .NET

P

Paul Selormey

There is many things in the design of the .NET type system
I do not understand why it should be like that:

1. Why are we not allowed to provide parameterless constructors
for value types? (I find it a large limitation, since I sometimes
need defaults/initialization other than the process provides)

2. Why is the ValueType derived from Object?

3. Since the derivation of ValueType from Object, makes it a class
why boxing?

4. Why is "ValueTypeArray" not provided instead of just Array for
everything?

5. Why is there no common base ValueType for numerics/numbers?

I recently found myself writing a class like,
public class TestClass
{
private ValueType m_valueMax;

public TestClass(ValueType value)
{
m_valueMax = value;
}

public ValueType Value
{
get
{
return m_valueMax;
}
}
}

instead of
public class TestClass
{
private object m_valueMax;

public TestClass(object value)
{
m_valueMax = value;
}

public object Value
{
get
{
return m_valueMax;
}
}
}

because I simply needed a number to be passed it, and do not
need to be doing stuff like

if (value == null)
throw new ArgumentNullException("value");

Best regards,
Paul.
 
J

Jon Skeet [C# MVP]

Paul Selormey said:
There is many things in the design of the .NET type system
I do not understand why it should be like that:

1. Why are we not allowed to provide parameterless constructors
for value types? (I find it a large limitation, since I sometimes
need defaults/initialization other than the process provides)

In some circumstances (such as array creation) you'd either have to
take a huge performance hit, executing every constructor (when often
you'd then want to write over the top of the array anyway) or you'd
have to accept that the constructor wasn't always called. I think this
would lead to confusion.
2. Why is the ValueType derived from Object?

Because all classes do, however...
3. Since the derivation of ValueType from Object, makes it a class
why boxing?

ValueType is indeed a reference type. However, things get tricky - the
types derived from ValueType are *actually* reference types, but
they're only the boxed types. The value types themselves are types with
the same name, but which don't actually derive from anything. It's all
a bit strange, but it's explained in the CLI spec.
4. Why is "ValueTypeArray" not provided instead of just Array for
everything?

What help would it be?
5. Why is there no common base ValueType for numerics/numbers?

As value types can't inherit from anything other than ValueType, that
would be hard. An interface might be useful, but only in some
situations.

<snip>
 
P

Paul Selormey

Hello Jon,
Thanks for the response.
In some circumstances (such as array creation) you'd either have to
take a huge performance hit, executing every constructor (when often
you'd then want to write over the top of the array anyway) or you'd
have to accept that the constructor wasn't always called. I think this
would lead to confusion.

Nice explanation, there is at least a reason :)

"In some circumstances", should be easy then. If the user provides the
parameterless constructor you use it, if not there is nothing there anyway!
Because all classes do, however...

I think this does explain anything...
ValueType is indeed a reference type. However, things get tricky - the
types derived from ValueType are *actually* reference types, but
they're only the boxed types. The value types themselves are types with
the same name, but which don't actually derive from anything. It's all
a bit strange, but it's explained in the CLI spec.

I might have to look into the CLI spec to see the picture, since I still do
not
see the need for this strange step.

It is even getting stranger with coming .NET 2.0's null structures. Why not
simply provide a IsNull method in the ValueType, which can be overriden
like the Equals method?
What help would it be?

If that might avoid boxing. I might boxing if I write?

ValueType dValue = 2.0d;

In a project, I had to create DoubleCollection, IntegerCollection etc.
As value types can't inherit from anything other than ValueType, that
would be hard. An interface might be useful, but only in some
situations.

We still have a special value type the "System.Enum", which even permits us
to
write (in C#)

public System.Enum N : short
{
}

so why not System.Number?
That could then give rise! to

public System.Double : System.Number
{
}

An interface will not be appropriate since we will then be dealing with
boxing issues again.

Best regards,
Paul.
 
S

Sean Hederman

Paul Selormey said:
There is many things in the design of the .NET type system
I do not understand why it should be like that:

1. Why are we not allowed to provide parameterless constructors for value
types? (I find it a large limitation, since I sometimes
need defaults/initialization other than the process provides)

The CLR actually does allow for parameterless (default) constructors, but C#
doesn't. If you have a look at the generated IL of structs and classes
(without constructors), you'll see that the C# compiler generates a default
constructor for classes, but not for structs. The C# compiler initializes
structs not by a default constructor but by setting all value type fields to
their default values, and all reference type fields to null (using the
..initobj IL instruction). So initializing a struct like this:
MyStruct a = new MyStruct();
would not call a default constructor (if it could have one), but would use
the .initobj instruction instead.

As to why this is so, read
http://www.dotnetconsult.co.uk/weblog/PermaLink.aspx/03805a0c-525f-4b7d-b0a0-f5f2773b4a7c
for more detail. basically it boils down to array initialization. If you
declare an array of 20 Foo's, then the system has to allocate memory for
that on the stack. For reference types this is easy, it just allocates 20
null references (which will later each point to the heap). However, a value
type is stored in the stack, right inside the array. That would mean that it
would have to create 20 structs, and call each one's default constructor. If
the constructor did something expensive and we change 20 to 20,000 you
suddenly have a major performance problem.

The point to remember about value types is that you're sacrificing a bit of
flexibility in return for large performance improvements.
2. Why is the ValueType derived from Object?

It provides a unified type system. Absolutely everything can be cast to
Object from which you can do a whole bunch of funky stuff.
3. Since the derivation of ValueType from Object, makes it a class
why boxing?

Whilst ValueType is inherited from Object, a value type is NOT. It is a
standard primitive much as you get in any other programming environment.
However you can use it as if it were inherited from Object. When you do this
however, the underlying value type is boxed, and all of a sudden appears to
inherit from Object.
4. Why is "ValueTypeArray" not provided instead of just Array for
everything?

It would be quite a pain to have two different array types. Either they'd
have to be completely unrelated, or they'd have to inherit from a common
interface. Said interface would HAVE to use Object params to be common
across both value and reference types, and therefore you'd still have boxing
occurring. In addition as I noted above, many value types (maybe all?) are
not actually derived from ValueType, and only appear to do so after a boxing
operation. So having a method like:
void Add(ValueType value)
will result in a boxing operation when you call it.

This issue is the cause of quite a few performance hits in the framework.
Luckily for us, generics will make many of these disappear like the morning
dew.
5. Why is there no common base ValueType for numerics/numbers?
[snip example]
The different number types are fairly disparate beasts. There's not one
method on Int32 for example that I can see that one could port to another
numeric type in a generic manner. So you'd have a base class with no methods
or properties. If the only thing the base class is adding is type
discrimination, then it's just overhead.

Why don't you rather have the following constructors:
public TestClass(int value){}
public TestClass(float value){}
public TestClass(double value {}
etcetera, etcetera
 
J

Jon Skeet [C# MVP]

Paul Selormey said:
Nice explanation, there is at least a reason :)

"In some circumstances", should be easy then. If the user provides the
parameterless constructor you use it, if not there is nothing there anyway!

But that forces all users of your struct to pay a performance penalty
every time they create an array - nasty.

I might have to look into the CLI spec to see the picture, since I still do
not see the need for this strange step.

It is even getting stranger with coming .NET 2.0's null structures. Why not
simply provide a IsNull method in the ValueType, which can be overriden
like the Equals method?

Because that being available would imply the need for extra space for
*every* value type. The ability to have an extra value (null) doesn't
come for free. It's also good to be able to express which values need
to potentially be treated specially.
If that might avoid boxing. I might boxing if I write?

ValueType dValue = 2.0d;

That's definitely boxing.
In a project, I had to create DoubleCollection, IntegerCollection etc.

That will be solved by generics, of course. However, without generics
you'd have a major problem - double is a different size from int (just
one example), so you couldn't possibly declare a variable to be of
"general value type" without either wasting loads of space or
restricting the values to only be those of a certain maximum size,
neither of which is very palatable.
We still have a special value type the "System.Enum", which even permits us
to write (in C#)

public System.Enum N : short
{
}

so why not System.Number?

That's C# syntax, but it doesn't *actually* make the enum derive from
short.

<snip>

Value types don't come with type information - just the value - so if
you declares something as being just "Number" there'd be no way of
knowing either how much space that variable should take up, or (after
allocating a value to it) what type the value should actually be.
 

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