Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Fixed Length (user defined length) Char Primitive Data Type #244

Open
WolvenRA opened this issue Jan 18, 2018 · 36 comments
Open

New Fixed Length (user defined length) Char Primitive Data Type #244

WolvenRA opened this issue Jan 18, 2018 · 36 comments
Labels
LDM Reviewed: No plans LDM has reviewed and this feature is unlikely to move forward in the foreseeable future

Comments

@WolvenRA
Copy link

While I suspect this may have been brought up before, since I'm new here and couldn't find any reference to it in the Issues, I'll suggest it.

Strings are great if you need an immutable variable length char data type... but they are not at all great when you want a mutable Fixed Length Char (FLC) variable. Besides being useful in their own right, a FLC would also allow Structures to be defined that have data in SPECIFIC well defined places.

It would also allow a natural VB.net implementation of Structures with overlapping data fields (Unions) which doesn't work with strings. Likewise with arrays of FLC's... You could overlay arrays of different element sizes, or overlay an array on a structure and do a lot of things that are nearly impossible to do now.

SQL has a user defined fixed length char type. Why shouldn't VB?

@ericmutta
Copy link

@WolvenRA ...but they are not at all great when you want a mutable Fixed Length Char (FLC) variable.

I second this idea, and I would generalize it to say mutable Fixed Length Primitive Arrays (FLPA), which is quite a mouthful 😄

Basically, having mutable arrays of integer, byte, char, etc that are allocated a constant amount of space within which they can shrink and grow. This is VERY VERY handy when dealing with binary data formats for storage in files or sending over networks via protocols like UDP (where packets shouldn't exceed roughly 1,500 bytes).

Example: you have a binary file split into blocks of 4096 bytes (i.e a 4KB page). Each block can contain data bytes which start with a 4-byte length indicator followed by the actual bytes. If you have for example 100 bytes of data, only part of the 4096 bytes of the block will be occupied. The remaining bytes are unused and remain reserved so you can later store 500, 800, 2,000 or however many bytes you want provided they don't exceed 4092 (so that 4 bytes for length indicator and 4092 bytes for data = 4096 total bytes for the block).

At the moment, the knowledge that an array can have a maximum of X bytes is implicit and known only to the programmer. So you get a lot of code using Buffer.BlockCopy with careful use of the index/offset/count parameters to move data into and out of pre-allocated buffers that should not change in size. Luckily, If you trip up in .NET you get an IndexOutOfRangeException. In the C/C++ world you get buffer overflows which lead to security exploits which lead to bad days for a lot of people.

@WolvenRA there's quite a lot of work to flesh out this idea (syntax for fixed-length arrays, conversion rules to/from variable-length arrays, etc). If you are up for it, let's hash it out here (I am sure anyone else who has had to deal with binary data will see the utility and pitch in).

@ericmutta
Copy link

ericmutta commented Jan 18, 2018

As a starting point (and borrowing from the tuples implementation) we can say fixed-length arrays are implemented as syntactic sugar over structures like these:

  Public Structure FixedArray1(Of T)
    Public Element0 As T
  End Structure

  Public Structure FixedArray2(Of T)
    Public Element0 As T
    Public Element1 As T
  End Structure

  Public Structure FixedArray3(Of T)
    Public Element0 As T
    Public Element1 As T
    Public Element2 As T
  End Structure

So lets say you wanted a mutable Fixed Length Char (FLC) variable as @WolvenRA mentioned earlier, you would do:

Dim chars = New FixedArray3(Of Char)

chars(0) = "a"c '<--- syntax sugar for: chars.Element0 = "a"c
chars(1) = "b"c '<--- syntax sugar for: chars.Element1 = "b"c
chars(2) = "c"c '<--- syntax sugar for: chars.Element2 = "c"c

chars = {"e"c, "f"c} '<--- syntax sugar for: chars.Element0 = "e"c: chars.Element1 = "f"c.

'the two lines below are semantically identical:
  Dim NormalCharArray As Char() = chars 
  Dim NormalCharArray = New Char(){chars.Element0, chars.Element1, chars.Element2}

'fixed length arrays have an implicitly defined constant called Length.
Console.WriteLine(chars.Length) '<--- prints 3 for FixedArray3(Of T)

The above is just for demonstration purposes to show the semantics. Once the fixed-length arrays get large (e.g. 4096 elements), it is impractical to think of a structure with thousands of fields. The actual implementation would use the regular arrays we have today, but with a layer to impose the length limits, copy data in/out, convert to/from regular arrays, etc.

Interestingly enough, it is possible to implement this in VB without touching the CLR or trying to coordinate across languages. The syntax sugar shown above would be translated into calls on a generic helper class that just wraps over a regular array. Such a class could ship via NuGet as stand-alone assembly much like System.ValueTuple.dll has been doing for a while.

In fact its probably worth exploring to see if the syntax for tuples can be extended to support indexing:

    Dim point = (x:=12, y:=13)
    Dim x1 = point.x  '<--- recently added support for named tuple elements.
    Dim x2 = point.Item1 '<--- old syntax for tuple access, same as point.x above.
    Dim x3 = point(0) '<--- NEW SYNTAX required for indexing, same as point.x and point.Item1

@WolvenRA
Copy link
Author

WolvenRA commented Jan 18, 2018

@ericmutta Thanks for the support, and I definitely agree with the Fixed Length Arrays as well.

I think we may be thinking slightly differently regarding the overlaying structures though.
As for the FLC themselves, I'm thinking we would define them like:

Dim CustName As Char Length 30   
Dim Zip As Char Length 10    
' or 
Dim CustName As FixedChar(30)   
Dim Zip As FixedChar(10)

As for overlaying structures, I'm looking for something more like:

Public Structure Overlayed1
    Public myFixedArray(39) As Char                  '<--- starts in position 0 of the structure

    Public CustName As FixedChar(30) Pos(0)    '<--- starts in position 0 of the structure
    Public FirstName As FixedChar(14)  Pos(0)    '<--- starts in position 0 of the structure, Overlays Name
    Public Initial As FixedChar(1)  Pos(14)           '<--- starts in position 14 of the structure, Overlays Name
    Public LastName As FixedChar(15)  Pos(15)   '<--- starts in position 15 of the structure, Overlays Name

    Public Zip As FixedChar(10)  Pos(30)       '<--- starts in position 30 of the structure
    Public Zip1 As FixedChar(5)  Pos(30)       '<--- starts in position 30 of the structure and Overlays Zip
    Public Zip2 As FixedChar(5)  Pos(35)       '<--- starts in position 35 of the structure and Overlays Zip

In the above scenario, myFixedArray(14) would be the same as Initial
myFixedArray(15) would be the first letter of LastName

For another example:

Public Structure Overlayed2
    Public Month1 As FixedChar(15)         '<--- starts in position 0 of the structure
    Public Month2 As FixedChar(15)         '<--- starts in position 15 of the structure
    Public Month3 As FixedChar(15)         '<--- starts in position 30 of the structure
    Public Month4 As FixedChar(15)         '<--- starts in position 45 of the structure
    Public Month5 As FixedChar(15)         '<--- starts in position 60 of the structure
    Public Month6 As FixedChar(15)         '<--- starts in position 75 of the structure
    Public Month7 As FixedChar(15)         '<--- starts in position 90 of the structure
    Public Month8 As FixedChar(15)         '<--- starts in position 105 of the structure
    Public Month9 As FixedChar(15)         '<--- starts in position 120 of the structure
    Public Month10 As FixedChar(15)         '<--- starts in position 135 of the structure
    Public Month11 As FixedChar(15)         '<--- starts in position 150 of the structure
    Public Month12 As FixedChar(15)         '<--- starts in position 165 of the structure

    Public myFixedArray(11) As FixedChar(15)  Pos(0)    '<--- starts in position 0 of the structure

In the above scenario, myFixedArray(3) would be the same as Month4

@ericmutta
Copy link

@WolvenRA I think we may be thinking slightly differently...

I was just about to comment and say apologies for jumping on this without knowing for sure the semantics you had in mind!

@WolvenRA SQL has a user defined fixed length char type. Why shouldn't VB?

One reason that comes to mind immediately is that VB is not concerned with on-disk binary data storage where issues of data length are a big deal. Maybe you could explain the scenario you are facing and how fixed-length char variables would help?

@WolvenRA
Copy link
Author

Obviously, I need a little practice with the "code" sections... :)

@WolvenRA
Copy link
Author

@ericmutta "I was just about to comment and say apologies for jumping on this without knowing for sure the semantics you had in mind!"

Don't worry about it. I think we're on the same page and for similar reasons.

@WolvenRA
Copy link
Author

WolvenRA commented Jan 18, 2018

@ericmutta

Another overlayed structure with overlayed arrays example;

Public Structure myStruct
    Public myBytes(19) As Byte Pos(0)
    Public myWords(4) As FixedChar(3) Pos (0)
    Public Byte1 As Byte Pos(0)
    Public Byte2 As Byte Pos(1)
    Public Byte3 As Byte Pos(2)
    ...
    Public Byte20 As Byte Pos(19)`

So myBytes(1) is Byte2,
myWords(1) is Byte5, Byte6, Byte7, Byte8

myWords(1) = "0000" 

That would set Bytes 5 to 8 to zero, it would also set myBytes(0) to myBytes(3) to zero as well.

@AdamSpeight2008
Copy link
Contributor

@WolvenRA The markup for vb code is ```vbnet at the of the block and then ``` at the end.

@WolvenRA
Copy link
Author

@AdamSpeight2008 Thanx Adam.

@WolvenRA
Copy link
Author

I realize I'm actually mixing two concepts here, Fixed Length Data Types and Structures with Overlapping Fields (Unions), but the latter requires the former.

For those programmers that have never had them they may not recognize the capabilities they provide. But for those of us that have, not having them in VB.net is quite annoying. Things that used to be "automatic" now requires a lot of extra coding to accomplish.

@ericmutta
Copy link

@WolvenRA I think we may be thinking slightly differently regarding the overlaying structures though.

Looking at your examples I can see now what you are getting at 👍

Unions I believe may be implemented by a combination of LayoutKind, StructLayoutAttribute and FieldOffsetAttribute . For example, this is shown on those links (the FieldOffset attribute specifies the precise location of each field and allows them to be overlapped):

<StructLayout(LayoutKind.Explicit)>
Public Class SYSTEM_INFO
    <FieldOffset(0)> Private OemId As System.UInt64
    <FieldOffset(8)> Private PageSize As System.UInt64
    <FieldOffset(16)> Private ActiveProcessorMask As System.UInt64
    <FieldOffset(24)> Private NumberOfProcessors As System.UInt64
    <FieldOffset(32)> Private ProcessorType As System.UInt64
End Class

But other stuff, especially the fixed-length arrays, could definitely use some built-in syntax support.

It is interesting to note that for over 60 years, we've had a way to describe the structure of entire programming languages, but we have limited methods for doing the same thing with binary data (e.g. there is no equivalent of regular expressions for parsing binary data like we can already do with strings....hey @AdamSpeight2008 there's a side-project idea for you since you seem to enjoy this "syntax/parsing" stuff 😄).

@WolvenRA
Copy link
Author

@ericmutta
Yes, you can do the sort of thing you outlined... But it's quite limited. Try adding a String field in the middle somewhere. As long as you use System types that have a fixed length, you're semi good. But you can't do what I've outlined above using Fixed Length Character variables. Also, try overlapping two Int32 with an Int64... Or try to add a Char or Byte array that overlaps the UInt64 fields you defined.

@reduckted
Copy link
Contributor

@WolvenRA a FLC would also allow Structures to be defined that have data in SPECIFIC well defined places.

The only reason you'd want to do this is for interop, right? Or is there a scenario that I'm not thinking of?

@WolvenRA
Copy link
Author

@reduckted
No, not just for interop. As a business application developer (think accounting software) I would use this all the time. For many years I programmed on IBM Power Systems running OS\400 which supports fixed data types and Unions. They are GREAT tools for working with data and a database.

SQL has fixed length data types. C\C++ has Unions. I'll post some example uses later.

@WolvenRA
Copy link
Author

@reduckted
One of the things I've noticed over my years of programming is that the "system" level programmers generally have no experience with business application programming. Consequently, they often don't see a reason for features that are quite useful for business programming... because they never need them for what they do.

While I don't want to seem overly critical, since switching to VB.net over a decade ago, it often seems like the developers were rather lazy... in the sense that simple features\functions they could include in the language, they leave it up to us VB.net programmers to write ourselves. A few examples;

Can't specify the number of elements in an array when defined in a Structure. Consequently we have to add a Redim statement somewhere else to set it.

Can't .Clear a Structure (i.e. set all fields in a structure to their default value). We have to write our own .Clear function Have to write our own Operator's for =, <>, for Structures to compare whether the Values of all the fields in one instance of a Structure are = to all the Values in another instance of the same Structure.

Can't convert a Datarow to a Structure and vice versa, even though they are virtually the same thing. (I wrote my own using System Reflection and some annoying data manipulation).

Can't define a Structure by using "Like" a Datarow, or another Structure, or a SQL Table Schema.

Can't .ToString a Structure (i.e. create a String of all the field values in the Structure)

Using .ToString on an Array (and lots of other things) returns the Array Type rather than a String of all the array element Values, like that's what anybody would want (or expect). If I wanted the Type, I would expect to write "myArray.Type.ToString".

And I could go on. Can I write my own funtions for these things? Yes. Should I have to? No.

On to examples of uses for Fixed Length data types and Unions. One common case is where I have a 9 digit Item number which actually represents three different 3 digit values, Category, Type, Seq. With fixed length data types and unions I could set it up like this (using some SQL type syntax);

Public Structure myStruct
    Public Item As Decimal(9, 0) Pos(0)
    Public Category As Decimal(3, 0) Pos(0)
    Public Type As Decimal(3, 0) Pos(3)
    Public Seq As Decimal(3, 0) Pos(6)
End Structure

Then, when I want the Item # I write "myStruct.Item"
If I want the Category I write "myStruct.Category", etc...

I realize I could create ANOTHER Structure like;

Public Structure Item
    Public Category As Integer
    Public Type As Integer
    Public Seq As Integer
End Structure

' Then
Public Structure myStruct
    Public myItem As Item
    Public ItemName As String
    ...
End Structure

But then, when I want the Category, I have to write "myStruct.myItem.Category". Not nearly as nice.

Likewise, the Item # may be part of a Key which also includes the Warehouse # (3 digit). Using unions I would have;

Public Structure myStruct
    Public Key As Decimal(12, 0) Pos(0)
    Public Warehouse As Decimal(3, 0) Pos(0)
    Public Item As Decimal(9, 0) Pos(3)
    Public Category As Decimal(3, 0) Pos(3)
    Public Type As Decimal(3, 0) Pos(6)
    Public Seq As Decimal(3, 0) Pos(9)
End Structure

So I can access Key, Warehouse, Item, Category, etc... all the same way "myStruct.whatever field I want"
Again I know I could do;

Public Structure Item
    Public Category As Integer
    Public Type As Integer
    Public Seq As Integer
End Structure

' Then
Public Structure Key
    Public Warehouse As Integer
    Public myItem As Item
End Structure

' Then
Public Structure myStruct
    Public myKey As Key
    Public ItemName As String
    ...
End Structure

So now, when I want the Category I have to write "myStruct.myKey.myItem.Category" Hopefully, it's beginning to become obvious that this quickly becomes tedious, and from my experience, unnecessary.

Another example. Overlaying a percentage amount to eliminate math instructions. Users like to see percentages like 90%, 75%, etc. But to use them in calculations we need to convert them to .90, .75, etc.

Public Structure myStruct
    Public Percent1 As Decimal(5, 2) Pos(0)
    Public Percent2 As Decimal(5, 4) Pos(0)
End Structure

So when I'm outputting the percent to the User I use Percent1. When I use it in a calculation I use Percent2. No math instructions to convert from the user version to the calculation needed.

Final example. Sales amount by month stored in SQL as Decimal(12, 2). (JanSales, FebSales, ... DecSales)
Using Unions;

Public Structure Sales
    Public MonSales(11) As Decimal(12, 2) Pos(0)
    Public JanSales As Decimal(12, 2) Pos(0)
    Public FebSales As Decimal(12, 2) Pos(12)
    ...
    Public DecSales As Decimal(12, 2) Pos(132)
End Structure

' Usage:
JanSales = 123456.78
FebSales = 234567.89
' etc...

TotSales = MonSales.Sum

' With tools I've had in the past I could also do.
Dim Sales_2016 LIKE Sales
Dim Sales_2017 LIKE Sales
Dim Sales_Tot LIKE Sales
Dim Sales_Dif LIKE Sales
Dim Sales_DifPercent(11) As Decimal(5, 2)

' Load or Set the individual sales values

Sales_Tot.MonSales = Sales_2016.MonSales + Sales_2017.MonSales
TotSales = Sales_Tot.Sum

Sales_Dif.MonSales = Sales_2017.MonSales - Sales_2016.MonSales
Sales_DifPercent = ((Sales_Dif.MonSales / Sales_2017.MonSales) * 100).Round(5, 2)

Capabilities like that make life much nicer for business application developers.

@reduckted
Copy link
Contributor

Can't specify the number of elements in an array when defined in a Structure. Consequently we have to add a Redim statement somewhere else to set it.

These answers on StackOverflow may give you some insight into why structures don't have default constructors:

Can't .Clear a Structure (i.e. set all fields in a structure to their default value). We have to write our own .Clear function

Set the structure to Nothing.

Public Structure Foo
    Public Bar As Integer
    Public Meep As String
End Structure

Dim x As Foo
x.Bar = 1
Assert.Equal(1, x.Bar)
X = Nothing
Assert.Equal(0, x.Bar)

Have to write our own Operator's for =, <>, for Structures to compare whether the Values of all the fields in one instance of a Structure are = to all the Values in another instance of the same Structure.

.Equals() works (though I believe there may be some limitations, such as with fields/properties that are reference types):

Dim x As New Foo
Dim y As New Foo
 
Assert.True(x.Equals(y))

x.Meep = "abc"
y.Meep = "def"

Assert.False(x.Equals(y))

x.Meep = "xyz"
y.Meep = "xyz"

Assert.True(x.Equals(y))

Can't define a Structure by using "Like" a Datarow, or another Structure, or a SQL Table Schema.

I don't understand what you mean by this. A DataRow is dynamic and can be defined at runtime, while a structure is static and must be defined at compile time. Anyway, that's probably off-topic for this issue, so don't worry about going into more detail about it 😄

Can't .ToString a Structure (i.e. create a String of all the field values in the Structure)

That's not specific to VB.NET. Any .NET language will do the same thing, so it seems a bit unfair to blame the VB designers for this.

Using .ToString on an Array (and lots of other things) returns the Array Type

As above. That's the way .NET behaves, so it's unfair to say that this is a problem with VB.NET and blame the VB designers.

Overlaying a percentage amount to eliminate math instructions. Users like to see percentages like 90%, 75%, etc. But to use them in calculations we need to convert them to .90, .75, etc.

You can use format strings for that. The format string even adds the percent sign for you.

Dim z As Decimal = 0.75D
Console.WriteLine(z.ToString("P0"))
' Outputs "75%"

Final example. Sales amount by month stored in SQL as Decimal(12, 2). (JanSales, FebSales, ... DecSales)
Using Unions;

I don't fully understand this example. I can see that your structure contains a fixed-length array of twelve elements, and then has twelve other fields that are essentially aliases for elements of the array. That makes sense.

But then you say TotSales = MonSales.Sum. Is .Sum the Enumerable.Sum() extension method? If so, that makes sense. If not, where does .Sum() come from?

Dim Sales_2016 LIKE Sales

Is this any different to Dim Sales_2016 As Sales?

' Load or Set the individual sales values

I think this is a key part that would be good to expand on. How are you loading the data. Are you simply assigning an array to MonSales? Or are you deserializing it from binary data? Or converting it from some other type (like a DataRow)?

Sales_Tot.MonSales = Sales_2016.MonSales + Sales_2017.MonSales

Are you adding two arrays together here? Is this supposed to create a new fixed-length array that adds each element from the first fixed-length array with the corresponding element from the second fixed-length array?

I'm not trying to dismiss this idea. I'm trying to get more information about it. 😄
Adding a feature to a language takes a lot of design and planning - after all, you only get one chance at it. So the more information you can provide about how it should work, the better. 👍

@WolvenRA
Copy link
Author

@reduckted
Hey, Thanx for the response and explanations.
First, I should say there are many things I like about VB.net. But I think it could be improved a lot which I'm sure is why you and the other people involved are investing your time here. I appreciate that.

Reading about Nothing pointed out that it will set any Reference type variables to Null. I use Strings in my Structures and having them set to Null each time I wanted to Clear the Structure would drive me nuts... I REALLY don't like Nulls. :)

It doesn't seem (from my ignorant perpective) like it would be much work to basically copy the logic that is executed when you do a "X = Nothing" on a Structure and just change it to set each variable to its Non-Null Default value rather than Nothing and then implement that as a ".Clear" method for Structures. Simple Right? :)

I found this in the Microsoft documentation when I was reading about the Equals method. Apparently the Equals method checks more than just the Values of the object.

"If you are implementing a value type, you should consider overriding the Equals method to gain increased performance over the default implementation of the Equals method on ValueType. If you override Equals and the language supports operator overloading, you should overload the equality operator for your value type."

Maybe a .ValuesEquals method would be good. I add a Public Shared Operator sub for = and <> to each of my Structures. It seems more natural (to me) to write

If Structure1 = Structure2 _
OrElse Structure1 <> Structure3 Then
   ...
End If

As for the ".ToString" problems I pointed out not being specific to VB, well, as our mothers used to ask "Would you jump off a cliff just because Johnny did?" :) Just because every other .net language didn't do it right doesn't seem like a good reason for VB to not do it right.

Was not aware of the P string formatter.

Yes, using LIKE as I did was a horrible example. A better one would be;

Friend myDT As New DataTable("myDT") LIKE Structure1

That would create datatable myDT with columns that matched the names and types of each variable in Structure1

In the example of the Sales by month JanSales, FebSales, etc are columns of a table in an SQL database. By putting them into a structure with an Array that "overlays" them it essentially "autoloads" the array with the values from the individual monthly sales. And yes, at that point the individual variables are essentially aliases for the corresponding MonSales array elements... or vice versa depending on how you care to look at it. Either way, changing the value of one also changes the other.

As to how the values get loaded into the JanSales, FebSales, (and their corresponding MonSales array element) varies a lot in a typical business application Edit program. All of the following could and generally would happen;
They get loaded from a datarow in a datatable that was used to retrieve information from a SQL database.
They get output to individual io textboxes on a Form. When the user saves the data on the screen they get set by the values of their corresponding textbox on the Form.
They may be used as a parameter to a method that calculates and sets them from detailed invoice data.

But the real point is, just as you pointed out, any change made to the individual Sales variables automatically updates the associated array (and vice versa). So that whenever I want, I can use EITHER the array (as in an Array.Sum method to get a total for the year) or use the individual variables (to add\update the SQL table) without ever having to assign the values from one to the other.

Sales_Tot.MonSales = Sales_2016.MonSales + Sales_2017.MonSales

"Are you adding two arrays together here? Is this supposed to create a new fixed-length array that adds each element from the first fixed-length array with the corresponding element from the second fixed-length array?"

Yes, that is what I'm doing. Although it's not "creating" a new array, it's simply setting each element of Sales_Tot.MonSales to the sum of each Sales_2016.MonSales + Sales_2017.MonSales elements. Likewise with the other math expressions using whole arrays. This is something I could do with the languages I previously programmed in.

In fact, ALL of the functionality I've presented in my previous posts are things I used to be able to do. As I think I mentioned before, for programmers that have never had these capabilities, they may not recognize their value. For those of us that have had them, we REALLY miss them. :)

@AdamSpeight2008
Copy link
Contributor

You could override the default implementation of .ToString method on your object.

@reduckted
Copy link
Contributor

just change it to set each variable to its Non-Null Default value rather than Nothing and then implement that as a ".Clear" method for Structures. Simple Right? :)

Simple? Nope. What is this "non-null default" value? String could be String.Empty I guess, For arrays it could be an empty array. But what is the non-null default for this type?

Public Class Foo
    Sub New(something As Bar)
    End Sub
End Class

This would also be a massive breaking change, so it will never happen.

They get loaded from a datarow in a datatable that was used to retrieve information from a SQL database.

So what does this database table look like? Because it seems like you'd still need to do some mapping to get the data from the DataRow into the array. Unless you're just using DataRow.ItemArray?

For what it's worth, DataTable (i.e. ADO.NET) and WinForms are pretty ancient technology. Entity Framework and WPF (combined with an MVVM design) is really the better approach these days. Of course, I realize there may be business limitations that mean you need to use this older technology. But I don't like the chances of adding new features to the language with the sole purpose of supporting this older, dated technology.

This is something I could do with the languages I previously programmed in.
...
In fact, ALL of the functionality I've presented in my previous posts are things I used to be able to do.
...
we REALLY miss them.

I'm curious. What language were you using previously, and if it's better suited for what you're trying to do, why not use it? I'm assuming there's some business decisions that required/forced you to move to VB.NET.

As I think I mentioned before, for programmers that have never had these capabilities, they may not recognize their value.

The same could be said for adopting things like Entity Framework and WPF ;)

@WolvenRA
Copy link
Author

@AdamSpeight2008
I could, but I shouldn't have to... :) Obviously, with enough dicking around I can (and do) get around the lack of all the functionality I've mentioned... but again, I shouldn't have to. In theory, I could just write my own language. :)

Ideally VB.net would provide the ability to write any type of program someone would want to. From hardware drivers and operating systems to business accounting systems. That said, IMO, the point of a high level language is to enable programmers to build their applications easily, intuitively and efficiently. In many ways VB does that. But in many other ways it doesn't, and for no apparent reason.

The .ToString method intuitively would return a string of the value(s) of whatever object you used it on. Unintuitively, sometime it does, sometimes it doesn't. I don't want sound like a curmudgeon, but IMO that's poor design. Likewise there seems to be a lot of functions that aren't the least bit intuitive, such as Math.Round(...) and Decimal.Round(...) Intuitively, I would expect to be able to write;

Dim X As Decimal
X = 15.12345
X.Round(2, MidpointRounding)

That's intuitive, natural, and how a lot of other methods\functions work. And, (again from my ignorance of the underlying code), it would seem that since there is a Rounding function, it would be quite simple to implement it using the intuitive syntax above. And yes, I'm sure I could write my own Extension or something to the Decimal type to implement the intuitive syntax... but I shouldn't have to. :)

@reduckted
Copy link
Contributor

From hardware drivers and operating systems

OK, this is one of the craziest ideas I've heard. 😆

@WolvenRA
Copy link
Author

@reduckted
You're cheating... :) My example was for a Structure not a Class! And a Structure, unless I'm missing something, currently doesn't have a .Clear method. And since it doesn't, why would that be a breaking change? I'm not trying to argue, I just don't understand the why part.

Yes, I should probably look at Entity Frameworks and WPF.

Many years ago I programmed in RPG on IBM computers (AS\400, I Series, Series I, Power). It had the features I've been talking about, but you can't program PC's with RPG. Then as the world moved to Windows and networks, I moved to ASNA's Visual RPG. It is very similar to VB prior to .net but with command names and concepts familiar to RPG programmers. It also had the features I've been talking about.

When .net came out, ASNA developed a .net version of their language. Unfortunately (and with lots of loud complaining from their customers), they dropped most of the functionality I've been discussing. (I'm sure these things are not simple to implement) I worked with their .net version for over a year and, along with many of their other customers, finally got fed up with hearing "Well VB doesn't do that" and decided that if they weren't going to provide any functionality that VB didn't have, I might as well switch to using all Microsoft development software.

I know after a number of years they finally did add at least some of the functionality we lost back in, but at that point I didn't want to have to relearn a different syntax again. More importantly, when you have applications with over 800 Thousand lines of code, you don't just decide to sit down and rewrite them without a VERY good reason, At the time I switched from VRPG.net to VB.net, I hadn't rewritten most of my applications from the non.net VRPG version to VRPG.net. So I wasn't going to be "throwing" away years worth of development work.

So, to really answer your question, Why should VB.net incorporate this functionality? I would assume the goal of any language developer would be to get as much of the developer community using their language as they could. Otherwise we might as well drop VB altogether and just force everyone to use C# if they want to use a .net language at all. I know there are many people that think that would be a good idea. But I, and I suspect you and the other developers here, don't.

From my perspective VB has a nice syntax (no curly braces) and, for many things, an intuitive syntax. But as I said before, it could be a lot nicer. And that's meant to be helpful criticism, not just criticism. The more intuitive and powerful it is, the more developers will choose it as their development language of choice. And that helps insure that I don't have to sit down and learn another new language at some point because VB just couldn't keep up or compete.

@WolvenRA
Copy link
Author

@reduckted
From hardware drivers and operating systems

OK, this is one of the craziest ideas I've heard. 😆

:) I said IDEALLY... :) So it might be a little difficult to write a driver or OS that needed the .net framework to function... A minor inconvenience... :)

@ghost ghost mentioned this issue Feb 15, 2018
@Nukepayload2
Copy link

Perhaps we should use C# for this scenario ? Because fixed-length buffer is an unsafe value type.
dotnet/csharplang#1314

Or, we can duplicate the C#'s fixed-length buffer design, regardless whether the buffer is unsafe.

Existing C# design

public fixed DXGI_RGB GammaCurve[1025];

Transform into VB

Public GammaCurve As DXGI_RGB * 1024

Metadata in VB

<FixedBuffer(GetType(DXGI_RGB), 1025)>
Public GammaCurve As FixedBuffer_1024(Of DXGI_RGB)
' Pack = 0 is the default packing and should result in indexable layout.
<CompilerGenerated, <EditorBrowsable(EditorBrowsableState.Never)>, UnsafeValueType, StructLayout(LayoutKind.Sequential)>
Public Structure FixedBuffer_1024(Of T) ' Use legal type name for compatibility.
    Private _e0, _e1, _e2, ... , e1024 As T
    ' We won't generate ByRef returns like c#.
    Default Public Property Item(index As Integer) As T
        Get
            If CUInt(index) <= 1024 Then
                Return Unsafe.Add(Of T)(_e0, index)
            Else
                Throw New IndexOutOfRangeException
            End If
        End Get
        Set(value As T)
            If CUInt(index) <= 1024 Then
                Unsafe.Add(Of T)(_e0, index) = value
            Else
                Throw New IndexOutOfRangeException
            End If
        End Set
    End Property
End Structure

@paul1956
Copy link

A few years ago I did a worked on a large Database import project in VB.ASP where we imported up to 2 Gigabyte's of data on the first of every month (per customer) from a very poorly defined US Government Medicare source and needed to filter private data before the upload. The very first thing we did was write .ToString for every record (Structure) type and there were 100's. I never understood why that was necessary, and what use was the .ToString that returned a type name. As for Union that is a very dangerous concept for most VB programmers when dealing with x86 and PowerPC underlying architectures. I am hoping someday Slice will be supported in VB and prove the same functionality as is being requested.

@WolvenRA
Copy link
Author

As for Union that is a very dangerous concept for most VB programmers when dealing with x86 and PowerPC underlying architectures.

How is that a "very dangerous" concept?

@paul1956
Copy link

The byte ordering are different. a 16 bit word in one is low byte, high byte and on the other it is high byte, low byte when laid out in physical memory.

@WolvenRA
Copy link
Author

If I'm not mistaken, F# uses Unions. I know C\C++ uses Unions. And both of those run on the same hardware as VB. So, I guess I still don't see how it could be a problem.

@WolvenRA
Copy link
Author

Back to the original point of this "Issue"... i.e. Fixed Length Char Data Type. I should have said Fixed Length Data Types. Char\String\Arrays\whatever. In support of those types, since they wouldn't be changing in size, doesn't that make them just a bit easier to handle in the programs address space? Since they're not changing size, they're not causing fragmentation of the memory. Since the total length of the variable doesn't change, isn't it easier to just change the value at a particular memory address (i.e. Fixed Length String) rather than Create, Copy, Change and then Delete the original?

And again, once we had (user set) Fixed Length Data Types, we could then properly implement Unions or "Explicitly Laid Out Structures" which would enable Overlapping Data Fields... which are very useful to application developers.

@IS4Code
Copy link

IS4Code commented Mar 15, 2018

Already proposed.

@paul1956
Copy link

@WolvenRA I don't think F# or VB runs on multiple CPU architectures with different memory layouts, C/C++ programmers have had to deal with this issue since the IBM/Digital and Intel/Motorola battles in the 70's-90's and have some features to help with the issues and it still trips up programmers when trying to run on new CPU's. Most programmers today weren't born when this was a big issue. With code running in the cloud you really have no idea what micro architecture your code might run on.

@HugoRoss
Copy link

HugoRoss commented Apr 5, 2018

@WolvenRA
For your information: It is possible in VB.NET to overlap Int32 and Int64 etc. Here an example of how to overlap an Int32, an UInt32 and 4 Bytes to define a structure of a pixel with Alpha, Red, Green and Blue values (but watch out when working on that level, depending on whether the processor of the computer this program is running on is little endian or big endian, the Int32/UInt32 values may be different).

Imports System
Imports System.Runtime.InteropServices

<StructLayout(LayoutKind.Explicit)>
Public Structure Argb

    'Constructors

    Public Sub New(signedValue As Int32)
        Me.SignedValue = signedValue
    End Sub

    Public Sub New(unsignedValue As UInt32)
        Me.UnsignedValue = unsignedValue
    End Sub

    Public Sub New(alpha As Byte, red As Byte, green As Byte, blue As Byte)
        Me.Alpha = alpha
        Me.Red = red
        Me.Green = green
        Me.Blue = blue
    End Sub

    Public Sub New(red As Byte, green As Byte, blue As Byte)
        Me.Alpha = 255
        Me.Red = red
        Me.Green = green
        Me.Blue = blue
    End Sub

    'Public Fields

    <FieldOffset(0)>
    Public SignedValue As Int32
    <FieldOffset(0)>
    Public UnsignedValue As UInt32
    <FieldOffset(3)>
    Public Alpha As Byte
    <FieldOffset(2)>
    Public Red As Byte
    <FieldOffset(1)>
    Public Green As Byte
    <FieldOffset(0)>
    Public Blue As Byte

End Structure

@IS4Code
Copy link

IS4Code commented Apr 5, 2018

That's nothing special to VB.NET. C# can use these attributes in the exactly same way.

@WolvenRA
Copy link
Author

WolvenRA commented Apr 6, 2018

@HugoRoss Thanks Hugo. Why are the Offsets backwards from the constructor definitions, i.e. Alpha is Offset(3) instead of Offset(0)? Is that due to endianess? Also, I want to be able to do the same thing with Char\String values, Arrays, Decimals, etc...

@IllidanS4 Yeah, but we're not talking about C#. If we're going to consider other languages, there are much nicer and cleaner ways of doing it than either VB.net or C#.net,

@KathleenDollard
Copy link
Contributor

As might be implied by the length of this thread, working out the details here would be a huge work item. It might be best implemented with changes to the CLR.

@KathleenDollard KathleenDollard added the LDM Reviewed: No plans LDM has reviewed and this feature is unlikely to move forward in the foreseeable future label Jun 13, 2018
@WolvenRA
Copy link
Author

@KathleenDollard
So how do we get it considered for CLR changes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LDM Reviewed: No plans LDM has reviewed and this feature is unlikely to move forward in the foreseeable future
Projects
None yet
Development

No branches or pull requests

9 participants