Knowledge Cube
Knowledge Cube

Reputation: 1010

How should I implement a custom numeric data type in Visual Basic .NET?

Visual Basic .NET provides several numeric value types built-in (Integer, Double, etc.), each with their own ranges of valid values. In most cases, these are fine for what I am doing.

However, sometimes I find myself wanting a numeric value type with my own custom range of valid values, among other things. Suppose I frequently work with values representing a quantity which can only ever be between two values (say, -20 to 500, as a hypothetical example), and are always an integer quantity. These requirements are enforced by the type everywhere it is used. Let's call this type SpecialInteger.

I'd like to be able to do things like this with my custom type:

...and so on. You get the idea.

However, I could not readily find anything online on how I should go about doing this. I'm guessing this will be implemented as some sort of Structure with a property which gets or sets the value, but I don't know what else is needed to fully and properly implement my type. Are there conventions to follow or interfaces required? Any advice, resources or how-to's would be appreciated.

Upvotes: 1

Views: 861

Answers (2)

TnTinMn
TnTinMn

Reputation: 11801

You can achieve your goals by defining various Operator Statements on your SpecialInteger type.

For the implicit type conversion between SpecialInteger and Integer, you need Widening operators. For addition, you need a + operator.

Something like this will get you started.

Public Structure SpecialInteger
    Const min As Int32 = -25
    Const max As Int32 = 500
    Private backing As Int32

    Public Sub New(value As Int32)
        Me.Value = value
    End Sub

    Public Property Value As Int32
        Get
            Return Me.backing
        End Get
        Set(value As Int32)
            If Validates(value) Then
                Me.backing = value
            Else
                Throw New ArgumentOutOfRangeException(String.Format("min: {0}, max: {1}, Value: {2}", min, max, value))
            End If
        End Set
    End Property

    Private Function Validates(value As Int32) As Boolean
        Return (min <= value) AndAlso (value <= max)
    End Function

    Public Shared Widening Operator CType(ByVal value As SpecialInteger) As Int32
        Return value.backing
    End Operator

    Public Shared Widening Operator CType(ByVal value As Int32) As SpecialInteger
        Return New SpecialInteger(value)
    End Operator

    Public Shared Operator +(v1 As Int32, v2 As SpecialInteger) As SpecialInteger
        Return New SpecialInteger(v1 + v2.backing)
    End Operator
End Structure

Upvotes: 4

Alex B.
Alex B.

Reputation: 2167

An own struct would be feasible.

I implemented something similar lately so I can give you some hints what could be helpful:

Public Structure SpecialInteger
    'private field to hold the integer value.
    Private _value As Integer

    'internal property with range check in the setter
    Friend Property Value() As Integer
        Get
            Return _value
        End Get
        Set
            If value > 500 Then
                Throw New OverflowException("Value must be <= 500")
            End If
            _value = value 'value from setter
        End Set
    End Property

    'If you want to compare two SpecialIntegers
    Public Overrides Function Equals(obj As Object) As Boolean
        If Not (TypeOf obj Is SpecialInteger) Then
            Return False
        End If
        Dim s = CType(obj, SpecialInteger)
        Return s.Value = Me.Value
    End Function

    Public Overrides Function GetHashCode() As Integer
        Return Value.GetHashCode()
    End Function

    Public Overrides Function ToString() As String
       Return Value.ToString()
    End Function

    'If you want to comapre with =, <> >, < or do math ops +, -, 
    Public Shared Operator =(s1 As SpecialInteger, s2 As SpecialInteger) As Boolean
        Return s1.Equals(s2)
    End Operator

    'same for <>
    Public Shared Operator <>(s1 As SpecialInteger, s2 As SpecialInteger) As Boolean
     '...
    End Operator

    Public Shared Operator <(s1 As SpecialInteger, s2 As SpecialInteger) As Boolean
        Return s1.Value < s2.Value
    End Operator

    'same for >
    Public Shared Operator >(s1 As SpecialInteger, s2 As SpecialInteger) As Boolean
           '...
    End Operator

    Public Shared Operator +(s1 As SpecialInteger, s2 As SpecialInteger) As Integer
        Return s1.Value + s2.Value
    End Operator

    'same for -
    Public Shared Operator -(s1 As SpecialInteger, s2 As SpecialInteger) As Integer
      '...
    End Operator

    'Dim si as SpecialInteger = 5
    Public Shared Widening Operator CType(s As SpecialInteger) As Integer
        Return s.Value
    End Operator

    'Dim i as Integer = si
    Public Shared Widening Operator CType(s As Integer) As SpecialInteger
        Return New SpecialInteger() With  {Key .Value = s }
    End Operator
End Structure

Some Tests:

Public Shared Sub Main(args As String())
    Dim someSpecialValue As SpecialInteger = 30
    Dim someInt As Integer = someSpecialValue

    Dim someSpecialValue2 As SpecialInteger = 10
    Dim sum As Integer = someSpecialValue2 + 4

    'The following line produces a runtime OverflowException:
    Dim someInvalid As SpecialInteger = 501

    'The following line produces a compile error:
    'SpecialInteger someInvalid2 = 1.5;

    Console.WriteLine(someSpecialValue.ToString()) '30
    Console.WriteLine(someInt.ToString()) '30
    Console.WriteLine(sum.ToString()) '14
    Console.ReadLine()
End Sub

P.S.: I wrote this in C# and used an online code converter to VB. Hope that everything is correct.

Upvotes: 1

Related Questions