Reputation: 11
Im trying to understand more about how value types are implemented and represented in C#, and was confused when I saw that the Single struct/value type is defined as so:
public struct Single : IComparable, IComparable<Single>, IConvertible, IEquatable<Single>, IFormattable
{
public const Single Epsilon = 1.401298E-45F;
public const Single MaxValue = 3.40282347E+38F;
public const Single MinValue = -3.40282347E+38F;
public const Single NaN = 0F / 0F;
public const Single NegativeInfinity = -1F / 0F;
public const Single PositiveInfinity = 1F / 0F;
// etc ...
}
How can the struct Single contain Singles in itself? Or in other words, how can the definition of type contain instances of that type? If I try to do this with my own struct, the compiler returns an error. Is is that the actual underlying values are C++ primitives, and the "value type" is just a collection of methods wrapped around it? I think ultimately Im confused by the distinction between "primitive" and "value type." If they are primitives, why are they defined as structs?
Upvotes: 1
Views: 200
Reputation: 16167
If you try this, the compiler complains that your struct cannot be made const
("The type 'MyStruct' cannot be declared const").
I asked a similar question about decimal
once upon a time. The answer, given by Jon Skeet himself, is that there are things in the source for mscorlib
that won't compile as-written without "some interesting hacks."
My guess is that you've come across one of these. Single
is a synonym of float
, which itself is a primitive type. The source is probably not a clean abstraction of the actual compiled/CLR type.
Upvotes: 2
Reputation: 9804
Any type can use itself in it's code.
This rule has been around since the early days of Object Oriented Programming. You can see it best in C++, where there is a explicit difference between Declarations and Definitions: http://www.cplusplus.com/articles/yAqpX9L8/
Basically you can declare that somewhere in this code (or another file), there is a definition for a "variable X". And can then use the "variable X", as if you had put it there with a include directive or fully defined. That way you can make something similar to the partial file.
For functions the process is called "prototyping".
Classes can kinda do it, within limits. In C++ you can not have a field of the type itself in itself, because that would mean each Instance would contain another isntance, causing a compiler recursion. And bloating up the type to infinite size. However you can have a pointer of the type. And in .NET we have references. And as both pointers and references can be null, there is no issue. It may contain a refrence recursion at runtime. But it does not at compile time and that is good enough for the compiler.
But these processes were nessesary, due to the Idiosyncracies of the Native C++ compilation method: Each file is compiled after the other. And include directives actually insert the code, really bloating the file Size between Pre-Processor and Compiler.
//Prototype of a function
double someFunction( double, int );
//Declaration of a variable
extern int y;
The thing is that by the time the C# compiler was made, they had evolved past the need for Prototypes and declarations. It can figure out that in this file or some other file there is a definition for the type "Single", so it can use the type "Single". Inlcuding the code of Single itself. Indeed it does so, every time you use a partial class - so every time you used a Windows Forms Designer.
With statics and constants, it is not even an issue:
Upvotes: 1