I have a problem with an Generic Class and define the operator overloads associated with it.
Question:
How do I set the operator for the following class assuming that the DataType will be any of the numeric types {float,double,Integer,etc.}?
I quite don't understand the concept yet
Public Class Nombre(Of DataType) '' aka Number
'VALUE
Private _Value As DataType
Public Property Value As DataType
Get
Return Me._Value
End Get
Set(value As DataType)
Me._Value = value
End Set
End Property
Public Sub New(value As DataType)
Me._Value = value
End Sub
Public Sub Add(x As DataType)
_Value += x
End Sub
Public Sub Subs(x As DataType)
_Value -= x
End Sub
Public Sub Mult(x As DataType)
_Value = _Value * x
End Sub
Public Sub Power(x As DataType)
_Value = _Value ^ x
End Sub
Public Sub inc()
_Value += 1
End Sub
Public Sub dec()
_Value -= 1
End Sub
Public Sub Divide(x As DataType)
_Value = _Value / x
End Sub
End Class
How do I set the operator for the following class assuming that the DataType will be any of the numeric types {float,double,Integer,etc.}?
You can't do that in a type-safe way, since you cannot restrict DataType to classes where +, -, etc. are defined. This is a commonly requested feature which is just not available yet.
You will have to create an IntegerNombre class, a DoubleNombre class, etc. Then you can define operator overloads in the usual way:
Public Shared Operator +(ByVal n1 As IntegerNombre,
ByVal n2 As IntegerNombre) As IntegerNombre
Return New IntegerNombre(n1._Value + n2._Value)
End Operator
(Of course, you could keep your generic class, turn Option Strict Off and use late binding with Object:
_Value = DirectCast(_Value, Object) + x
...but you really shouldn't.)
Related
I'm trying to create a class that can be CType'd to different types:
Option Strict Off
Public Class CInteger
Private Value As Integer
Public Sub New(value As Integer)
Me.Value = value
End Sub
Public Shared Widening Operator CType(ByVal value As CInteger) As Integer
Return value.Value
End Operator
'Commenting next operator makes compilation without errors
Public Shared Widening Operator CType(ByVal value As CInteger) As String
Return value.Value
End Operator
End Class
Public Enum EEnum1 As Integer
First = 1
End Enum
Public Enum EEnum2 As Integer
First = 1
End Enum
Private Sub Test()
Dim e1 As EEnum1 = New CInteger(1)
Dim e2 As EEnum2 = New CInteger(1)
End Sub
It works fine converting to Enums if there's only and Integer operator defined, but when I try to add any other operator the compiler throws errors because the conversion can't be done.
I can neither change the Enum declarations nor declare CType operator for each Enum, is it posible to workaround this?
I have a class that contains a property "value" that contain any data type and therefor is an object. In the included example, the "value" is the type "nonNegativeInteger". I need to be able to cast the structure to double and the only way I have found to do it is the ungainly:
CDbl(CTypeDynamic(obj, GetType(Double)))
here is the code in a simple vb windows forms project:
Option Strict On
Option Explicit On
Imports System.Dynamic
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim av = New anyValue
av.value = 4
Dim obj As Object = av 'box av
Dim dAv = CDbl(av) 'works
'Dim dObj = CDbl(obj) 'fails (invalid cast)
'Dim fAv = getDouble(av) 'fails
'Dim fObj = getDouble(obj) 'fails
Dim kAv = getDoubleKludge(av) 'works
Dim fObj = getDoubleKludge(obj) 'works
Stop
End Sub
Private Function getDouble(obj As Object) As Double
Return CDbl(obj)
End Function
Private Function getDoubleKludge(obj As Object) As Double
Return CDbl(CTypeDynamic(obj, GetType(Double)))
End Function
End Class
Public Class anyValue
Private _value As Object
Public Property value As Object
Get
Return _value
End Get
Set(obj As Object)
_value = obj
End Set
End Property
Public Shared Widening Operator CType(obj As anyValue) As Double
Return CDbl(obj.value)
End Operator
End Class
Public Structure nonNegativeInteger
Private _value As Integer
Public Property value As Integer
Get
Return _value
End Get
Set(val As Integer)
If val >= 0 Then
_value = val
Else
_value = 0
Throw New ArgumentOutOfRangeException
End If
End Set
End Property
Public Sub New(i As Integer)
If i >= 0 Then
_value = i
Else
_value = 0
Throw New ArgumentOutOfRangeException
End If
End Sub
Public Sub New(s As String)
If Not Integer.TryParse(s, _value) OrElse _value < 0 Then
_value = 0
Throw New ArgumentOutOfRangeException
End If
End Sub
Public Shared Widening Operator CType(ByVal o As nonNegativeInteger) As String
Return o.ToString
End Operator
Public Shared Narrowing Operator CType(ByVal s As String) As nonNegativeInteger
Return New nonNegativeInteger(s)
End Operator
Public Shared Widening Operator CType(ByVal o As nonNegativeInteger) As Double
Return CDbl(o.value)
End Operator
Public Shared Narrowing Operator CType(ByVal d As Double) As nonNegativeInteger
If d > Integer.MaxValue OrElse d < 0 Then
Throw New ArgumentOutOfRangeException
Return Nothing
Else
Return New nonNegativeInteger(CInt(d))
End If
End Operator
Public Overrides Function ToString() As String
Return _value.ToString()
End Function
Public Function toDouble() As Double
Return CDbl(_value)
End Function
End Structure
I have a class that inherits from the CollectionBase and when adding items I want to detect whether the collection already contains the key that is going to be inserted. If it does I want to send a warning via a MsgBox(). Here is the code & what I've tried
<Serializable()> Public Class validationList
Inherits CollectionBase
Public Function Add(ByVal Item As validationItem) As Integer
MsgBox(Me.List.Contains(Item))
Return Me.List.Add(Item)
End Function
Default Public ReadOnly Property Item(ByVal index As Integer) As validationItem
Get
Return CType(List.Item(index), validationItem)
End Get
End Property
Public Sub Remove(ByVal index As Integer)
Me.List.RemoveAt(index)
End Sub
Public Function IndexOf(ByVal key As validationItem)
Return List.IndexOf(key)
End Function
Public Sub AddRange(ByVal item() As validationItem)
For counter As Integer = 0 To item.GetLength(0) - 1
List.Add(item(counter))
Next
End Sub
End Class
<Serializable()> Public Class validationItem
Private _key As validationTypes
Private _value As String
Public Enum validationTypes
man = 0
num = 1
End Enum
Public Property Value As String
Get
Return _value
End Get
Set(ByVal Value As String)
_value = Value
End Set
End Property
Public Property Key As validationTypes
Get
Return _key
End Get
Set(ByVal value As validationTypes)
_key = value
End Set
End Property
Public Sub New()
' Empty constructor is needed for serialization
End Sub
Public Sub New(ByVal k As validationTypes, ByVal v As String)
_key = k
_value = v
End Sub
End Class
Public Class textbox
Inherits System.Windows.Forms.TextBox
Private _validation As New validationList
<System.ComponentModel.DesignerSerializationVisibility(Content)>
Public Property validation As validationList
Get
Return _validation
End Get
Set(ByVal value As validationList)
_validation = value
End Set
End Property
End Class
In the add method I tried to check whether the collection already has this item. But it always returns -1.
Here is code that adds a new item to the collection
Textbox1.validation.Add(New validationItem With {.Key = validationItem.validationTypes.man, .Value = "1"})
To make Contains work, you'll have to implement Equals/GetHashCode on validationItem or implement the IEquatable(Of T) interface:
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable(Of T).Equals method for T (the type of values in the list).
Here's an example implementation for Equals/GetHashCode that checks both, Key and Value:
<Serializable> _
Public Class validationItem
Protected Overloads Function Equals(other As validationItem) As Boolean
Return _value = other._value AndAlso _key = other._key
End Function
Public Overrides Function Equals(obj As Object) As Boolean
If obj Is Nothing Then
Return False
End If
If Me Is obj Then
Return True
End If
If obj.GetType() IsNot Me.GetType() Then
Return False
End If
Return Equals(DirectCast(obj, validationItem))
End Function
Public Overrides Function GetHashCode() As Integer
Return ((If(_value IsNot Nothing, _value.GetHashCode(), 0)) * 397) Xor CInt(_key)
End Function
...
End Class
You could also use use LINQ, here's an example that only checks for Key:
Public Function Add(ByVal Item As validationItem) As Integer
If Me.List.OfType(Of validationItem).Any(Function(i) i.Key = Item.Key) Then
' Do something '
Else
Return Me.List.Add(Item)
End If
End Function
You need to just check for whether the key exist or not and only show it if it exists:
Public Function Add(ByVal Item As validationItem) As Integer
If Me.List.Contains(Item) Then MsgBox("The key already exists")
Return Me.List.Add(Item)
End Function
As it stands you are just returning the result of the Contains method which is a Boolean (hence the -1)
I have a class that inherits from CollectionBase. I tried to use the contains method to detect whether the Key already exists before inserting a new one. Here is what I have tried.
<Serializable()> Public Class validationList
Inherits CollectionBase
Public Function Add(ByVal Item As validationItem) As Integer
Return Me.List.Add(Item)
End Function
Default Public ReadOnly Property Item(ByVal index As Integer) As validationItem
Get
Return CType(List.Item(index), validationItem)
End Get
End Property
Public Sub Remove(ByVal index As Integer)
Me.List.RemoveAt(index)
End Sub
Protected Overrides Sub OnInsert(ByVal index As Integer, ByVal value As Object)
If Me.List.Contains(value) Then MsgBox("Already exist")
MyBase.OnInsert(index, value)
End Sub
Public Function IndexOf(ByVal key As validationItem)
Return List.IndexOf(key)
End Function
Public Sub AddRange(ByVal item() As validationItem)
For counter As Integer = 0 To item.GetLength(0) - 1
List.Add(item(counter))
Next
End Sub
End Class
<Serializable()> Public Class validationItem
Implements IEquatable(Of validationItem)
Private _key As validationTypes
Private _value As String
Public Sub New()
' Empty constructor is needed for serialization
End Sub
Public Sub New(ByVal k As validationTypes, ByVal v As String)
_key = k
_value = v
End Sub
Public Enum validationTypes
Madatory = 0
[Integer] = 1
Numeric = 2
[Decimal] = 3
MaxValue = 4
MinValue = 5
MinLength = 6
Email = 7
End Enum
Public Property Value As String
Get
Return _value
End Get
Set(ByVal Value As String)
_value = Value
End Set
End Property
Public Property Key As validationTypes
Get
Return _key
End Get
Set(ByVal value As validationTypes)
_key = value
End Set
End Property
Protected Overloads Function Equals(ByVal eqItem As validationItem) As Boolean Implements IEquatable(Of Testing_Project.validationItem).Equals
If eqItem Is Nothing Then Return False
Return Me._key = eqItem.Key
End Function
Public Overrides Function Equals(ByVal eqItem As Object) As Boolean
If eqItem Is Nothing Then Return False
Dim eqItemObj As validationItem = TryCast(eqItem, validationItem)
If eqItemObj Is Nothing Then Return False
Return Equals(eqItemObj)
End Function
Public Overrides Function GetHashCode() As Integer
Return Me._key.GetHashCode()
End Function
End Class
The validationList will be exposed from a usercontrol as a property, so that items could be added from the designer. When adding items I need to detect whether they already exist. I tried overriding the OnInsert but this sometime return that duplicates exists even when their aren't and doesn't report that duplicate exist when I try to add existing keys.
This indirectly answers the question after dealing with the issue which emerged in comments about Collection(Of T):
Add a reference to System.Collections.ObjectModel if needed, then
Imports System.Collections.ObjectModel
' a ValidationItem collection class
Public Class ValidationItems
Inherits Collection(Of ValidationItem)
Public Shadows Sub Add(NewItem As ValidationItem)
' test for existence
' do not add if it is not unique
Dim dupe As Boolean = False
For n As Int32 = 0 To Items.Count - 1
If Items(n).Key = NewItem.Key Then
dupe = True
Exit For
End If
Next
If dupe = False then
items.Add(newitem)
End if
' I would actually use an IndexOfKey function which might
' be useful elsewhere and only add if the return is -1
If IndexOfKey(NewItem.Key) <> -1 Then
Items.Add(newItem)
End If
End Sub
Some NET collection types implement Add as a function and return the item added. This sounds weird since you pass it the item to add. But returning Nothing if the item cannot be added is a neat semaphore for "I cant/wont do that". I cant recall if the std NET Collection Editor recognizes that or not.
One problem with using Contains is that it will test if item passed as param is the same object as one in the list. They never will be the same object, even if they have the same values. Testing the key in a loop is simpler than calling a method which implements an interface. (That previous answer was totally valid in the context presented, but the context has changed).
Even if you stay with CollectionBase, you want to handle it in the Add. If you try to remove it in OnInsert, VS will have problems deserializing the collection.
Also, your validationitem needs a Name property or the Collection Editor will display "Myassembly+MyType" as the Name (or a ToString override).
Other issues:
I am not sure your IndexOf will work. The list contains ValidationItems (objects), but you check it for _key (string). This will not matter if you change to Collection(Of T) which implements it for you.
The simple ctor is needed by the Collection Editor, not serialization. But the important thing is that it is there.
As for the comment about all Zeroes coming back - that is because your ValidationItem is not yet decorated for designer serialization. Maybe not the Collection Property either, that isnt shown.
I have a problem that is frustrating me to no end, I have a overrideable function in a parent class, and the override function in the child class, like below:
sub class
Public Overrides Sub UpdatePrice(ByVal dblRetailPrice As Double)
If dblWholesalePrice < 0 Then
MessageBox.Show("Error, amount must be greater than 0.")
Else
dblRetailPrice = dblWholesalePrice * dblStandardMargin
End If
End Sub
and in the parent class, i have
Public ReadOnly Property RetailPrice() As Double
Get
Return dblRetailPrice
End Get
End Property
Public Overridable Sub UpdatePrice(ByVal dblRetailPrice As Double)
If dblWholesalePrice < 0 Then
MessageBox.Show("Please input an amount greater than 0,wholesale price has not changed", "error")
Else
dblRetailPrice = 1.1 * dblWholesalePrice
End If
End Sub
When i debug, the value is produced, but it doesn't carry over to the parent class of ski.RetailPrice() , what seems to be the problem here? Any help would be greatly appreciated.
You shouldn't be passing in a parameter with the same name as a class-level variable at a higher scope. The local variable will override the other, meaning that this statement in your setter:
dblRetailPrice = 1.1 * dblWholesalePrice
will set the value of the dblRetailPrice temporary parameter that you just passed in, not your class-level dblWholesalePrice member variable.
The simple solution is to change the name of the parameter by dropping the useless type notation prefix:
Public Class MyClass
Protected dblRetailPrice As Double
Public ReadOnly Property RetailPrice() As Double
Get
Return dblRetailPrice
End Get
End Property
Public Overridable Sub UpdatePrice(ByVal retailPrice As Double)
If dblWholesalePrice < 0 Then
MessageBox.Show("Please input an amount greater than 0,wholesale price has not changed", "error")
Else
dblRetailPrice = 1.1 * dblWholesalePrice
End If
End Sub
End Class