[.net] .NET 2.0 Bug

Started by
12 comments, last by Programmer One 15 years, 10 months ago
I think I found a bug in .NET 2.0 (not sure about 3.0 and 3.5) but I can't see to find any articles about this. I am working on something that heavily involves reflection, calling functions in classes that do not exist during compile time, etc, etc. As I was doing this, I found this interesting....quirk: So check this out. I am going to show the code for reproducing the bug as well as explain it. So. All classes, types and such are derived from System.Object. For example:

System.String -> System.Object
System.IO.MemoryStream -> System.IO.Stream -> System.MarshalByRefObject -> System.Object
So, you get my point. Now check this out:

System.Int32 -> System.ValueType -> System.Object
If I create a delegate whose return type is System.Object and then return any instance of System.String, System.IO.MemoryStream...then .NET has absolutely no problems. If you will notice that System.IO.MemoryStream is 3 levels deep in inheritance. So, my delegate has a return type of System.Object, but when I implement this delegate and the return type is System.String or System.IO.MemoryStream or System.GrandmaSpecialCake, we're all fine and dandy. HOWEVER If I try to return a System.ValueType such as System.Int32 or System.Double, .NET has a hissy fit. No no no, you can't do that. Because.....pffft, fuck you. System.Int32 is only 2 levels deep into inheritance from System.Object. Let me show some code:

// Declare some delegate with its return type as System.Object
public delegate object SomeDelegate();



SomeDelegate myDelegate = new SomeDelegate(FunctionReturnsString);
...
myDelegate();
...
// .NET Has Not Problems with This:
public String FunctionReturnsString()
{
    return "123";
}



SomeDelegate myDelegate = new SomeDelegate(FunctionReturnsStream);
...
myDelegate();
...
// .NET Has Not Problems with This:
public System.IO.MemoryStream FunctionReturnsStream()
{
    return new MemoryStream();
}



SomeDelegate myDelegate = new SomeDelegate(FunctionReturnsInt32);
...
myDelegate();
...
// .NET Shits Its Pants:
public Int32 FunctionReturnsInt32()
{
    return 123;
}


So yeah. Interesting stuff. Don't know if this still happens in .NET 3 or 3.5. EDIT: I should mention that the exception occurs when I am creating the delegate that points to these functions. [Edited by - Programmer One on June 11, 2008 6:01:20 PM]
Advertisement
Yeah, I don't think that's a bug. You can still return the integer if you want, just change the return value of your function back to "object", and then just return the boxed integer.

My guess is that the compiler can match up other functions that are classes (ie. reference types) because all references will end up being the same size at runtime (essentially a pointer). Value types, on the other hand, can be any size imaginable. How can the runtime ensure that things get passed correctly?

Instead, you just need to handle the boxing to a reference on your own. Not a big deal really, and I'm pretty sure it's not a bug, since the compiler appropriately errors out, and prevents you from breaking anything at runtime.
Mike Popoloski | Journal | SlimDX
Yeah, that doesn't leap out at me as a bug. Seems like mildly expected behavior if you've any background with unmanaged languages and/or have done some fiddling with delegates under the hood.

Have you tried returning a non-trivial value type? Say... System.DateTime I'd expect that to be more likely to work (though still iffy).
Quote:Original post by Telastyn
Yeah, that doesn't leap out at me as a bug. Seems like mildly expected behavior if you've any background with unmanaged languages and/or have done some fiddling with delegates under the hood.

Have you tried returning a non-trivial value type? Say... System.DateTime I'd expect that to be more likely to work (though still iffy).


I tried it, as well as my own structs. It just doesn't want to match the delegate up with functions that return value types. Which is understandable, since the function signature, from a runtime and compiler view, looks nothing like one that returns a reference type.
Mike Popoloski | Journal | SlimDX
myeh. delegates kinda suck.

Seems like the OP is stuck with some specialization with generic delegates or some adapter to change the return into a nullable...
The hierarchy is deceptive; a value type is a value type, not a reference type. That distinction is more important and more fundamental to the language that the derivation from System.ValueType which is more conceptual in nature.

I don't think this is a bug.
Well...

There just something about it that just doesn't seem right. I mean, if everything within .NET is supposed to be a subclass of System.Object, even System.ValueType, it would lend itself to the belief that what I am trying to do would work. Even on the conceptual level.

Seems quirky.
It's as much as a bug as the following code ;)

int i = null;

The CLR manages value types and reference types in different ways, no matter if they are both subclasses of System.Object (that's why they are allocated on the stack or the heap and why you can't do somethings sometimes).

Regards,

Vicente
My 2.0 cents:

The ValueTypes do not inherit from Object.
The reason you can cast a value type to an object and back again is something called boxing and unboxing, NOT inheritance.




Also: ValueTypes are not always allocated on the Stack (as many presume they do) that only happens when the variable is declared in a method or as a parameter. A valuetype that is a member of an class will be allocated in-line in the object on the heap. But this is a side track.
Quote:Original post by ernow
My 2.0 cents:

The ValueTypes do not inherit from Object.
The reason you can cast a value type to an object and back again is something called boxing and unboxing, NOT inheritance.


Your two cents are worthless. ValueType (the base type for all structs) does indeed inherit from System.Object. The reason you can cast a value type to an object is indeed from inheritance. It's how the language works. You can't magically up cast to classes that aren't your parents.

Now, in the case of value types on the stack, you're right that they do, in fact, need to be boxed to be referenced as an implemented interface or the base object class. This is different from the actual cast, however. Boxing merely creates a copy of the data on the managed heap and returns a reference to it. It's the actual casting that allows it to be usable as a different type.
Mike Popoloski | Journal | SlimDX

This topic is closed to new replies.

Advertisement