Reputation: 1010
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:
Assign Integer
values to my type:
Dim someSpecialValue As SpecialInteger = 30
If you try to assign it a value that's outside the range I mentioned above, throw an OverflowException:
Dim notAllowedValue As SpecialInteger = 501 ' Invalid value.
Throw something like an InvalidCastException if you try to assign a value that's within the range, but not a whole number:
Dim alsoNotAllowedValue As SpecialInteger = 1.5 ' Also invalid.
Have SpecialInteger
recognized as something like a special case of Integer
so I could continue to use it in assignment and arithmetic expressions like so:
Dim someInt As Integer
Dim myOtherValue As SpecialInteger = 234
' Neither of these statements should produce an error:
someInt = myOtherValue
someInt = myOtherValue + 5
...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
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
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