Visual-Basic-Record-Generator 1.2.3

There is a newer version of this package available.
See the version list below for details.
The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package Visual-Basic-Record-Generator --version 1.2.3
NuGet\Install-Package Visual-Basic-Record-Generator -Version 1.2.3
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Visual-Basic-Record-Generator" Version="1.2.3" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Visual-Basic-Record-Generator --version 1.2.3
#r "nuget: Visual-Basic-Record-Generator, 1.2.3"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Visual-Basic-Record-Generator as a Cake Addin
#addin nuget:?package=Visual-Basic-Record-Generator&version=1.2.3

// Install Visual-Basic-Record-Generator as a Cake Tool
#tool nuget:?package=Visual-Basic-Record-Generator&version=1.2.3

VB Record Source Generator

An amazing new feature called Source Generators has been added to VB.NET since VS.NET 16.9. It allows you to write code to generate another code that is added to your project in compilation time. You can combine this feature with the Roslyn compiler powerful tools (like SyntaxFacyoty, SyntaxTree and SemanticModel) to parse, compile and analyze VB syntax and generate any additional code you want. As an application of these concepts, I created a syntax for VB Records (quite similar to C# records). It consists of a Class/Structure declaration followed by a parameter list, followed by an optional Inherits statement. You can also add Imports statements at top of the file. These all 4 parts are valid VB syntax parts, but I grouped them to compose the new record syntax. This allows me to use Roslyn to parse my syntax as if it is a formal VB syntax, which made my write the code generator easily. These are three possible variations of the record syntax:

Public Record NameValue(Name ="", Value = 0.0)

Public ReadOnly Structure ROStruct(X$, Y%, Z@)

Friend ReadOnly Class ROClass(A As Integer, B As Integer)

#To use the Record Generator:

  1. Add the NuGet package to your project.
PM> Install-Package Visual-Basic-Record-Generator
  1. Add one or more text files to your project, and change there extension to .rec.
  2. Right-click the .rec file in the Solution Explorer and click Properties, then from the 'Build Action' dropdown list choose VB analyzer additional file and save the changes.
  3. Write the one or more record syntax in each .rec file and save the changes. The generator will generate the record classes/structures immediately for you, and you can use them in your code as a part of your project.

Record Syntax:

Simply, it is a class definition followed by a parameter list. For example:

<Record>
Public Class Person(
    ID = 0, 
    Name As String = "", 
    Address = (City := "", Street := "", No :=0)
)

Or just use the Record keyword for simplicity:

Public Record Person(
    ID = 0, 
    Name As String = "", 
    Address = (City := "", Street := "", No :=0)
)

This is a C#-like Person record class, that has three properties (ID, Name and Address). You can use an As clause to define the property type, or you can just give it an initial value, and the generator will infer the type from it. And of course you can do both. You can also define methods in the record and it will be converted to full body Subs or Functions. Look at this:

Public Class Student(
    <ReadOnly>ClassRoom = 0,
    <Key>University As String,
    <ReadOnlyKey>Collage As String,
    Grades As double, 
    Print = Function() Name & Grades
) Inherits Person

Or just use keywords for simplicity: Public Class Student( ReadOnly ClassRoom = 0, Key University As String, ReadOnly Key Collage As String, Grades As double, Print = Function() Name & Grades ) Inherits Person


The Student class inherits the Person class, and has a Print() method.
Note how I use the  attributes/ keywords Key, ReadOnly and ReadOnlyKey (ReadOnly Key) to mark the properties. A key property is a property that will be examined when determining the equality of two records. 
In C# record all properties are keys, but in our vb record we have the option to define our keys. You have many options here:
1. Mark the whole class with the `<Record>` attribute (or use the Record keyword instead of the Class keyword) to tell the generator that it is an immutable class where all its properties are ReadOnly Keys.
2. Mark the whole class with the `ReadOnly` attr/keyword to make all properties readonly but not keys then mark some individual properties with `Key` attr/keyword. 
3. Mark the whole class with the `Key ` attr/keyword to make all properties keys then mark some individual properties with `ReadOnly` attr/keyword.
4. Don't mark the class with any attr and use individual attrs/keywords to design your properties access as you need.

Note that the class attrs overrides property attrs.
So, you have all the options on the table, as you are not forces to generate immutable classes only, and not forced to use all properties as keys, but still can do both with one `Record` attr/keyword.

You can also use generic type parameters after the name of the class such as:
```VB.NET
Class Foo(Of T)(X As T, Y As T)

I wrote almost no code to get that working. It is the magic of Roslyn! You can look at the code at GitHub, and have fun.

Using the generated records:

VB doesn't have init-Only properties, so, you have to use ReadOnly properties instead. You can initialize ReadOnly properties via the constructor, but you cant use the With {} initializer to set them. To deal with limitation, I made all the constructor params Optional, so, you can use named params to set any properties in any order: Dim std1 As new Student(name:="Adam", ID:="1") I also added a With method to allow you to modify some properties of a new copy of the record. It is like the cinstructor, with all params optional, so, it acts as the new with expression in C#: Dim std2 = std1.With(name:="Ali", iD:="2", university=:"Cairo university") I also added a WithX method to set each property individually: Dim std3 = std2.WithId(3).WithGrades(79) You can also use this with chain to initialize a new record like this:

Dim Mohmmad = New Student( ).
    WithID(4).       
    WithName("Mohmmad").
    WithCollage("Engineering").
    WithUniversity("Cairo University")

And don't forget, you can design your recotrd to have some mutable properties, so, you can use the With {} initializer on them.

Sending Nothing to optional params:

I allow you to set a default values for record properties, where I use the constructor to set them if the corresponding optional params are missing. I use this way because VB compiler refuses using any default value for the optional param unless it's a constant, and the only constant valid for objects is Nothing. So, I had to use Nothing as the default value of the param, and wrote an If statement to set the actual default value you provided instead of Nothing. The problem here is: what if you send the value Nothing to the parameter and really want to reset the property to Nothing? Unfortunately, this will not happen, because the If statement will replace nothing with the default value! To solve this issue, I defined all params to be Optional(Of T). This structure is like the nullable structure, with HasValue and Value properties. It works as follows:

  • when the optional param is missing, or you send the value Nothing to the param, HasValue = false.
  • when you send new [Optional](Of T)(Nothing) to the param, HasValue = True and Value contains Nothing!
  • You can pass any normal value of type T directly to the param and it will be implicitly concerted to Optional(Of T), and will be implicitly concerted back to T when setting the property value.

In fact you don't have to worry about all these details, because it is used internally in the generated class. All that you want to know, is that when you need to send Nothing to any object, all you have to do is send one of these alternatives:

  1. new [Optional](Of T)(Nothing)
  2. [Optional](Of T).Nothing
  3. new [Nothing](Of T) Where T is the type of the property. Note that this is not needed with value types, as I use the Nullable structure instead. It is needed only with value-type params of the With methods, so I can set it's default vakue to Nothing (an actual nothing that will be restored in the nullable sturcture), as an indication of that param is missing, so I copy the value of the property from the current record (Me), to the new returned record.
There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.2.0 174 12/13/2022
2.1.0 165 3/8/2022

# Ver 1.2:
 - Using nullable params for ValueTypes, and using Optional(T) params with nullable types and ref types.
# Ver 1.1:
 - Supports using Record key word instead of <Record> Class.
 - Supprts using Ky, ReadOnly (Or Immutable) and ReadOnly Key (Or Immutable Key)  as keywords instead of Attrs.
 - Supports using any ,net or user-defined attrs with the class it's properties.
 - Supports using ?, (), and type chars after the property name.
 - Enhanced ToString method to show nested records, and list elements.

# Ver1.0:
 - Supports writing methods as lampda expressions.
 - Supports Inheritance.
 - Supports type inference for properties with default Values.