Click here to Skip to main content
14,932,440 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Len(datatypeInstance) in VB.NET (sizeof in C#) does return the number of bytes each primitive value type requires in storage. Len() documentation.

Works wonderfully.

Unless the datatype is Decimal.

Instances of datatype Decimal occupy 16 bytes, as per documentation.

Only that it doesn't return 16, but 8 bytes.

Is this intentional, or a, uhm, feature? How would I obtain 16 bytes?

What I have tried:

Len(1D) does return 8. Yes, D is official, see suffixes.

Len(CType(1, Decimal)) does also return 8. Unlikely for a primitive, but perhaps Decimal is ambiguous? Let's qualify the datatype fully.

Len(CType(1, System.Decimal)) does also return 8.

The first example with 1D is explicitly using Decimal, the other 2 cast to Decimal.
Updated 27-Feb-21 6:47am
[no name] 27-Feb-21 9:11am
in c# int decSz = sizeof(Decimal); ends in decSz == 16
Visual Herbert 27-Feb-21 9:42am
Thanks for confirmation! So this might indeed be a VB bug having been undiscovered for decades? Hm.
[no name] 27-Feb-21 10:02am
You are welcome. Personally I think it is more a misunderstanding/misinterpretation than a bug from VB. But I have to admit I do not know VB.

1 solution

You're comparing apples to oranges. The sizeof operator in C# has no equivalent in VB.NET.

In C#, the sizeof(decimal) is NOT an evaluation of the size of a type. It's a known constant value determined at compile-time, not run-time. In the documentation for sizeof():
The sizeof operator requires an unsafe context. However, the expressions presented in the following table are evaluated in compile time to the corresponding constant values and don't require an unsafe context

At compile time, sizeof(decimal) is simply replaced with the constant value 16. The same would be true for any of the primitive types:
.method private hidebysig static void  Main(string[] args) cil managed
  // Code size       27 (0x1b)
  .maxstack  2
  .locals init ([0] int32 l)
  IL_0000:  nop
  // int l = sizeof(decimal);
  IL_0001:  ldc.i4.s   16
  IL_0003:  stloc.0
  IL_0004:  ldstr      "size is {0}"
  IL_0009:  ldloc.0
  IL_000a:  box        [mscorlib]System.Int32
  IL_000f:  call       string [mscorlib]System.String::Format(string,
  IL_0014:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0019:  nop
  IL_001a:  ret
} // end of method Program::Main

Now, in VB.NET, the documentation for Len() says "Returns an integer containing either the number of characters in a string or the nominal number of bytes required to store a variable. ... With user-defined types and Object variables, the Len function returns the size as it will be written to the file by the FilePut function..

It's not evaluating the size of the variable in bytes. This is not a bug.

In VB.NET, what you should be doing is l = Marshal.SizeOf(x), which will return the correct value of 16. But, notice, this time it's not a constant replacement value. It's a function call to an external method that does the evaluation:
.method public static void  Main() cil managed
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       31 (0x1f)
  .maxstack  2
  .locals init ([0] valuetype [mscorlib]System.Decimal x,
           [1] int32 l)
  IL_0000:  nop
  IL_0001:  ldloc.0
  // l = Marshal.SizeOf(x)
  IL_0002:  call       int32 [mscorlib]System.Runtime.InteropServices.Marshal::SizeOf<valuetype [mscorlib]System.Decimal>(!!0)
  IL_0007:  stloc.1
  IL_0008:  ldstr      "Length is {0}"
  IL_000d:  ldloc.1
  IL_000e:  box        [mscorlib]System.Int32
  IL_0013:  call       string [mscorlib]System.String::Format(string,
  IL_0018:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001d:  nop
  IL_001e:  ret
} // end of method Module1::Main

If you want the actual number of bytes, evaluated at run-time, in both languages, you would need to call Marshal.SizeOf().
[no name] 27-Feb-21 13:07pm
Cool one, my minor 5
BillWoodruff 27-Feb-21 21:51pm
Visual Herbert 28-Feb-21 2:02am
I appreciate your effort, really, but I can not accept this answer as the solution.

Yes, in order to obtain the size of a value type (structure), it is oftentimes recommended to use the Marshal.SizeOf method. However, reading the method's documentation states:

Returns the unmanaged size, in bytes, of a class.

and then doubles on with:

The size returned is the size of the unmanaged object. The unmanaged and managed sizes of an object can differ.

As the .NET's value types surely are managed, using Marshal.SizeOf is simply wrong. And it is also inefficient.

Whereas the documentation for Len() states:

Returns an integer containing either the number of characters in a string or the nominal number of bytes required to store a variable.

From Decimal.GetBits it is absolutely clear, that the struct contains 4 x Int32, for a total of 16 bytes. Returning 8 is simply... wrong.
Dave Kreskowiak 28-Feb-21 11:06am
You didn't read ALL of the documentation on Len(), which is intended for strings, not returning the bytes for variables! That alone should tell you that there is behavior in Len you do not understand and the documentation does not make really clear.

The reference source for Len is not available so it's not possible to compare the source code of the Len and Marshal.SizeOf functions.

If you won't accept that, there's nothing else anyone is going to be able to tell you that's going to satisfy you.

Again, you're comparing apples to oranges by expecting one function to act just like the other. VB.NET has no equivalent of C#'s sizeof().

You did not discover a bug in VB.NET, but only in less than adequate documentation.

Did you think of the possibility that there is no solution?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900