none
Create a new Value data type RRS feed

  • General discussion

  •  


    I all 

     I have a bit of time to kill, so I will write some Bla-Bla here

    So let talk about "How to create a new Value data type"

    ---------------

    First a note:  The method I will use here is the method used by the Framework to create a Value type (ie Boolean, Int32, Byte, etc...). This method is not only friendly to .. , but also supported by the CLR and any .NET compiler (Otherwise it wouldn't work!)

    ---------------

    Since Boolean already exist as a bi-Value, I will create here a Tri-Value. Let call it "UpDown".

    "UpDown" will be so that it can take 3 possible values: Up, Flat or Down

    ----------------

    The first thing that we need is a class that will let us "Box" the new type we will create.

    To create such class, there is only one way it is to use the keyword "Structure" (We will see later why)

       Well, I can see that some of you are thinking "A Structure is not a Class"

    Well, actualy, yes, a structure is a class that is used to box a value and interact with it

    As proof if I use this code:

     Public Structure UpDown
       End Structure

    Once compiled, I will have

       .class public sequencial ansi sealed WindowsApplication.UpDown 
              Extends [mscorlib]System.ValueType
        {}

    where "[mscorlib]System.ValueType" is declared as:

        .class public auto ansi abstract serializable beforefieldinit System.ValueType
               extends System.Object

    (It is interresting here to note that even if the class System.ValueType is marked "public" and "abstract" it will be impossible to inherits from it. The VB compiler does make a special case with this class and will not let you do it.) 
    (C# compiler does the same)

    The keyword "Structure" was made with the only purpose of creating a class that inherits from System.ValueType
    (Note: keyword "Enum" is the only other way to inherits from Value Type)

    Now, lets come back to this first step and create this class like this. We will look at the implementation later

        Friend Structure UpDown
        End Structure


    -----------
    The second step to take is to  expose the possible values that this type can take.

       In a compiler defined data type, these value are exposed in the compiler assembly, not in the application assembly. 

    This gives the compiler the possibility to hardcode the values into the compiled code and therefore create a faster runtime.

       Here, since it is not a compiler defined value type (And that we don't want to re-write the VB compiler) we will need to use a module  to expose these values. It is basicaly the same process, just that the comparaisons are done at runtime instead than being done at compile time.

    So, let for now just create this module without all the needed implementation.

        Friend Module DefUpDown
           Public Up as UpDown
           Public Flat as UpDown
           Public Down as UpDown
        End Module
     


    -----------

    At this point, before we start to do any implementation, the following things need to be knew.

    I have said that we have to use a structure, to create the class that will be used to do the boxing. 
       Why ?

    The reason is that the created class will inherits from ValueType,... And the functionality of ValueType that is is needed here is that ValueType does overrides the method Equals to perform a bitwise comparaison of the objects instead of comparing the value of the instance pointer. So, two objects, located at two different location in memory will be considered equals if they are bitwise the same. 

    Another point that is needed to be noticed here is that a value, when passed as argument, will ALWAYS be boxed

    ---------

    Let look at what we have so far, 3 values, Up, Down and flat. And these 3 values are equal since the comparaison that the assembly does is a bitwise comparaison.

    For this to be usefull, need to create a difference between them.

    I will here follow the way that was used by the boolean Value to create a difference between True and False and create an internal, private integer property. 

    The use of Integer as internal type is motivated by the fact that when we will create a new UpDown this internal integer will initialize to zero. and by setting one of the exposed value of UpDown to the pattern "InternalValue = 0" we will create a default value (This is why, in the Boolean data type, True can be anything, but False has to be zero)

    So, let create this private value into the structure


       Friend Structure UpDown
          Private Property InternalValue As Integer
       End Structure

    --------

    Now, we are facing a problem. 

    To initialize the internal value of Up, Down and Flat, we need to use reflection, but as I said earlier, when a value is passed as parameter, it is always boxed so using

       PropertyInfo.SetValue(Up, 1, Nothing)

    will not work, since the class "Up" passed as argument to the method SetValue is the "box" class, not the actual value that we want to changed. Therefore, using SetValue as it is usualy done will only change the internal value in the "box" object, and as soon the method SetValue will return, the created "box" will get out of scope and the value will be lost (this is because the arguments are boxed, but not unboxed).

    To solve the problem, we will have to take the control of the boxing process and explicitly do the boxing/unboxing of the argument 

    Let see that:
     -We will set the internal value of:  Up to 1,   Down to -1,   Flat to 0  (So flat will be the default value)

     -To do the boxing and unboxing,  there is more than one way, but "DirectCast" is the most strait forward way)

    We can therefore initialized our values this way

        Public Up As UpDown =
            New Func(Of UpDown)(Function()
                                    Dim Flags As Integer = 36 'NonPublic Or Instance
                                    Dim UD As UpDown
                                    Dim T As Type = UD.GetType
                                    Dim P As Reflection.PropertyInfo = T.GetProperty("InternalValue", CType(Flags, Reflection.BindingFlags))
                                    Dim Box As ValueType = DirectCast(New UpDown, ValueType) 'Boxing a UpDown 
                                    P.SetValue(Box, 1, Nothing)
                                    Return DirectCast(Box, UpDown) 'Unboxing
                                End Function).Invoke
    
        Public Flat As New UpDown
    
        Public Down As UpDown =
            New Func(Of UpDown)(Function()
                                    Dim Flags As Integer = 36 'NonPublic Or Instance
                                    Dim UD As UpDown
                                    Dim T As Type = UD.GetType
                                    Dim P As Reflection.PropertyInfo = T.GetProperty("InternalValue", CType(Flags, Reflection.BindingFlags))
                                    Dim Box As ValueType = DirectCast(New UpDown, ValueType) 'Boxing a UpDown 
                                    P.SetValue(Box, -1, Nothing)
                                    Return DirectCast(Box, UpDown) 'Unboxing
                                End Function).Invoke

    You will note that the value {Flat} do not need any initialization since it is the default value 

    ------------------

    At this point, there is only one mandatory step that left to be done, it is to implement the operator = and <>

    This will be easy since our boxing class inherits from ValueType and therefore, as I said, already contains the 

    implementation for the required bitwise equality camparaison of the objects.

    we can therefore simply do:

      Public Shared Operator =(ByVal First As UpDown, ByVal Second As UpDown) As Boolean
            If UpDown.Equals(First, Second) Then Return True
            Return False
        End Operator
    
        Public Shared Operator <>(ByVal First As UpDown, ByVal Second As UpDown) As Boolean
            If UpDown.Equals(First, Second) Then Return False
            Return True
        End Operator


    -----------
    Now, there is 2 more thing that should be done, However not absolutely necessary, it is to implement a CType operator 

    and a ToString method.

    It can be done, as example, like this

        Public Shared Narrowing Operator CType(ByVal First As UpDown) As Integer
            Return First.InternalValue
        End Operator
    
        Public Overrides Function ToString() As String
            Select Case Me.InternalValue
                Case 1 : Return "Up"
                Case -1 : Return "Down"
            End Select
            Return "Flat"
        End Function


    ----------

    Ok, Let see what we can do now as far as usage of this code

    ( Well, let me add another operator in the structure so we have something to play with in the usage

        Public Shared Operator &(ByVal First As UpDown, ByVal Second As UpDown) As UpDown
            Dim Val As Integer = First.InternalValue + Second.InternalValue
            Select Case True
                Case Val > 0 : Return Up
                Case Val = 0 : Return Flat
                Case Val < 0 : Return Down
            End Select
        End Operator
                                                             


    ---------- USAGE----------------------

       Let create an UpDown

     Dim UpDown1 as updown

       Note: Updown1 is a {Flat} since it was not initialized by our code (it is therefore initialized as to be Default value)

    Now we can do

               UpDown1 = Up 

       We here have assing the value {Up} to our UpDown (Note: There is no possible way to assign any other value than 

    {Up}, {Down} or {Flat} to UpDown1, the compiler will not accept it)

      Let test the bitwise equality comparer

               If UpDown1 = Flat  then ... [ evaluate to false ]

               Dim UpDown2 as UpDown = Down
               If UpDown1 = Updown2 then ... [evaluate to false ]

      Since in our implementation UpDown can cast to integer and an integer can cast to boolean

               If CBool(Up) then ... [evaluate to true (Boolean only evaluate to false for 0 ( so for {Flat})]

      And testing our last operator & that we implemented


            Dim HiUp as UpDown = Up
            Dim LoDown as UpDown = Down
            Dim U = Up & Down & HiUp & LoDown    [ So, U = {Flat} ]



    -----------------

    The Code All together:

    Friend Structure UpDown
    
        Private Property InternalValue As Integer
    
        Public Shared Operator =(ByVal First As UpDown, ByVal Second As UpDown) As Boolean
            If UpDown.Equals(First, Second) Then Return True
            Return False
        End Operator
    
        Public Shared Operator <>(ByVal First As UpDown, ByVal Second As UpDown) As Boolean
            If UpDown.Equals(First, Second) Then Return False
            Return True
        End Operator
    
        Public Shared Narrowing Operator CType(ByVal First As UpDown) As Integer
            Return First.InternalValue
        End Operator
    
        Public Overrides Function ToString() As String
            Select Case Me.InternalValue
                Case 1 : Return "Up"
                Case -1 : Return "Down"
            End Select
            Return "Flat"
        End Function
    
        Public Shared Operator &(ByVal First As UpDown, ByVal Second As UpDown) As UpDown
            Dim Val As Integer = First.InternalValue + Second.InternalValue
            Select Case True
                Case Val > 0 : Return Up
                Case Val = 0 : Return Flat
                Case Val < 0 : Return Down
            End Select
        End Operator
    
    End Structure
    
    
    Friend Module DefUpDown
    
        Public Up As UpDown =
            New Func(Of UpDown)(Function()
                                    Dim Flags As Integer = 36 'NonPublic Or Instance
                                    Dim UD As UpDown
                                    Dim T As Type = UD.GetType
                                    Dim P As Reflection.PropertyInfo = T.GetProperty("InternalValue", CType(Flags, Reflection.BindingFlags))
                                    Dim Box As ValueType = DirectCast(New UpDown, ValueType) 'Boxing a UpDown 
                                    P.SetValue(Box, 1, Nothing)
                                    Return DirectCast(Box, UpDown) 'Unboxing
                                End Function).Invoke
    
        Public Flat As New UpDown
    
        Public Down As UpDown =
            New Func(Of UpDown)(Function()
                                    Dim Flags As Integer = 36 'NonPublic Or Instance
                                    Dim UD As UpDown
                                    Dim T As Type = UD.GetType
                                    Dim P As Reflection.PropertyInfo = T.GetProperty("InternalValue", CType(Flags, Reflection.BindingFlags))
                                    Dim Box As ValueType = DirectCast(New UpDown, ValueType) 'Boxing a UpDown 
                                    P.SetValue(Box, -1, Nothing)
                                    Return DirectCast(Box, UpDown) 'Unboxing
                                End Function).Invoke
    
    End Module




    • Edited by Crazypennie Wednesday, August 8, 2012 11:06 AM
    Wednesday, August 8, 2012 12:23 AM

All replies

  • Crazy,

    I've said before and will again: I wish I knew 1/100th what you know about this stuff!


    Please call me Frank :)

    Wednesday, August 8, 2012 2:09 AM