Comparing list of class by string length and text comparison - vb.net

I have a List(Of Abbreviation).
The class "Abbreviation" contains the string members "Input", "Output" and "CaseSensitive".
The class is stated below.
I would like to sort this list so that the class with the "Input"
"ZZZ"
comes before
"zz"
The comparison should thus first compare by string length, then by alphetical order, and then by CaseSensitive.
How could I sort the list this way?
Public Class Abbreviation
Implements IComparable
Private _sIn As String = String.Empty
Private _sOut As String = String.Empty
Private _bCaseSensitive As Boolean = False
Public Property Input() As String
Get
Return _sIn
End Get
Set(value As String)
_sIn = value
End Set
End Property
Public Property Output() As String
Get
Return _sOut
End Get
Set(value As String)
_sOut = value
End Set
End Property
Public Property CaseSensitive() As Boolean
Get
Return _bCaseSensitive
End Get
Set(value As Boolean)
_bCaseSensitive = value
End Set
End Property
Public Sub New(ByVal uInput As String, ByVal uOutput As String, ByVal uCaseSensitive As Boolean)
_sIn = uInput
_sOut = uOutput
_bCaseSensitive = uCaseSensitive
End Sub
End Class

First, you can simplify your code by using automatic properties. The compiler writes the get, set, and backer fields (the private fields). This is the preferred way in recent versions of Visual Studio if there is no extra code in the getter, setter.
Another small detail with your Sub New. It violates encapsulation to set the private fields directly. Always go through the Public Properties. There could be code in the setter that needs to run before storing the data in the private fields. Classes like to keep their data close to the vest in their private fields.
Example of Auto Implemented Properties
Public Property Input As String
Public Property Output As String
Public Property CaseSensitive As Boolean
The sort can be done in one line of code using a Linq query.
Dim orderedList = From abrev In lstAbreviations Order By abrev.Input.Lenght Descending Select abrev
To check the output...
For Each abrev As Player In orderedList
Debug.Print(abrev.Input)
Next

I think I got it, except the case sensitivity. CaseSensitive should come before CaseSensitive = False.
Public Class Abbreviation
Implements IComparable(Of Abbreviation)
Private _sIn As String = String.Empty
Private _sOut As String = String.Empty
Private _bCaseSensitive As Boolean = False
Public Function CompareTo(uOther As Abbreviation) As Integer _
Implements IComparable(Of Abbreviation).CompareTo
If uOther.Input.Length > Me.Input.Length Then
Return 1
ElseIf uOther.Input.Length < Me.Input.Length Then
Return -1
Else
If uOther.Input > Me.Input Then
Return 1
Else
Return -1
End If
End If
End Function

Related

How can I use textbox textchanged events to change user input classes?

InputValue1,..OperationAdd are instances of the class InputNumber. How do I assign my Inputvalues to the corresponding textboxes as option Strict On is activated?
Class MainWindow
Dim InputValue1 As New InputNumber
Dim InputValue2 As New InputNumber
Dim ExpectedResultValue As New InputNumber
Dim OperationAdd As New InputNumber
Private Sub TBoxNumber1_TextChanged(sender As Object, e As TextChangedEventArgs) Handles TBoxNumber1.TextChanged
TBoxNumber1.Text = InputValue1 'There is an error in this line, value of type InputNumber 'cannot be converted to a string
End Sub
Below is the InputNumber Class:
Public Class InputNumber
Inherits Input
Private _number As Integer
Public Property Number As Integer
Get
Return _number
End Get
Set(value As Integer)
_number = value
End Set
End Property
Protected Overrides Function Validate(s As String) As Boolean
Dim isValid As Boolean = Integer.TryParse(s, _number)
Return isValid
End Function
End Class
It seems to me that you should be getting the input from the TextBox into your InputNumber object, not the other way around. This:
TBoxNumber1.Text = InputValue1
should probably be this:
InputValue1.Validate(TBoxNumber1.Text)
When it actually comes time to display the value of that InputNumber, you would have to use InputValue1.Number.ToString(). Personally, I would add this to your InputNumber class:
Public Overrides Function ToString() As String
Return Number.ToString()
End Function
and then you can use InputValue1.ToString() instead.
EDIT:
It's been pointed out that the Validate method is Protected so it can't be called like that. Unless there's some use for it in the Input base class, I'm not sure what it's for because, as it stands, you'd need to validate externally anyway, e.g.
Dim number As Integer
If Integer.TryParse(TBoxNumber1.Text, number) Then
InputValue1.Number = number
End If
I am questioning the implementation of InputNumber. It does not make much sense in the current form. We don't know when validation is happen and Number is integer in fact.
I would do something like this.
Public Class InputNumber
Private _number As Integer
Private _hasNumber As Boolean
Public Sub New(s As String)
SetNumber(s)
End Sub
Public Sub New()
End Sub
Public ReadOnly Property Number As Integer
Get
Return _number
End Get
End Property
Public ReadOnly Property HasNumber As Boolean
Get
Return _hasNumber
End Get
End Property
Public Sub SetNumber(s As String)
_hasNumber = Integer.TryParse(s, _number)
End Sub
' For consistency...
Public Sub SetNumber(i As Integer)
_hasNumber = true
_number = i
End Sub
Public Overrides Function ToString() As String
If HasNumber Then Return Number.ToString()
Return String.Empty
End Function
End Class
Then your usage will be (and I agree with #jmcilhinney, when you have TextChange event, you want to take a value from the text box and set a variable)
Dim inp As New InputNumber("ddd")
txtBox1.Text = If(inp.HasNumber, inp.Number.ToString(), "NO VALUE") '' example to use HasNumber
inp.SetNumber("sss")
txtBox2.Text = inp.ToString() '' Example to use straight value
inp.SetNumber(10)
Dim current As Integer = If(inp.HasNumber, inp.Number, -1) '' using as numeric value
Now, this class makes slightly more sense

How to support contextual implicit conversions of custom object in Visual Basic .NET?

I want to use named error codes within my app. This should ensure, that every developer does not confuse numeric-only error codes with other codes, and also reduces the time a developer needs to realize what the error code should represent.
Compare this example:
Function New() As Integer
Return 0
End Function
with this example:
Function New() As Integer
Return ErrorCodes.ERROR_SUCCESS
End Function
Of course, I could let the developers write like the following:
Function New() As Integer
Return 0 ' ERROR_SUCCESS
End Function
However, the code above raises a pitfall when a developer updates the actual return code but forgets about the comment. Some developer look at the actual return code and some at the comment. I want to mitigate that confusion.
I come up the following class (extract):
Public Class ErrorCodes
Private msName As String = Nothing
Private miValue As Integer = 0
Public Shared ReadOnly ERROR_SUCCESS As ErrorCodes = New ErrorCodes("ERROR_SUCCESS", 0)
Private Sub New(ByVal psName As String, ByVal piValue As Integer)
msName = psName
miValue = piValue
End Sub
Public ReadOnly Property [Name] As String
Get
Return msName
End Get
End Property
Public ReadOnly Property [Value] As Integer
Get
Return miValue
End Get
End Property
Public Overrides Function ToString() As String
Return String.Format("[{0}]{1}", msName, miValue)
End Function
End Class
Now I want to use this ErrorCodes class like in the following example:
Function New() As Integer
Return ErrorCodes.ERROR_SUCCESS
End Function
As expected, I will produce an exception (type conversion) since the actual value I return is a instance of the class ErrorCodes instead of the generic data type Integer.
As you can see with the ToString() function, I let the class automatically/implicitly converts the instanced object into the generic data type String, when the class instance is assigned to a String typed variable.
Is there a way to do the same with the generic data type Integer like I did with ToString()?
I am using the .NET Framework 4.0, as for compatibility reasons with Windows XP SP3.
Another way to say what I want:
Dim stringVariable As String = ErrorCodes.ERROR_SUCCESS ' should be "[0]ERROR_SUCCESS".
Dim integerVariable As Integer = ErrorCodes.ERROR_SUCCESS ' should be 0.
I do not want to trigger implicit conversion warnings/errors, or to force the developer to typecast explicitly.
Yes you can do that with the use of Conversion Operators.
Here is the code:
Public Class Form1
Public Class ErrorCodes
Private msName As String = Nothing
Private miValue As Integer = 0
Public Shared Widening Operator CType(ByVal ec As ErrorCodes) As String
Return ec.ToString
End Operator
Public Shared Narrowing Operator CType(ByVal ec As ErrorCodes) As Integer
Return ec.Value
End Operator
Public Shared ReadOnly ERROR_SUCCESS As ErrorCodes = New ErrorCodes("ERROR_SUCCESS", 0)
Public Shared ReadOnly ERROR_FAILED As ErrorCodes = New ErrorCodes("ERROR_FAILED", 1)
Private Sub New(ByVal psName As String, ByVal piValue As Integer)
msName = psName
miValue = piValue
End Sub
Public ReadOnly Property [Name] As String
Get
Return msName
End Get
End Property
Public ReadOnly Property [Value] As Integer
Get
Return miValue
End Get
End Property
Public Overrides Function ToString() As String
Return String.Format("[{0}]{1}", msName, miValue)
End Function
End Class
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim em As String = ErrorCodes.ERROR_SUCCESS
Dim ev As Integer = ErrorCodes.ERROR_SUCCESS
Dim mm As String = String.Format("String: {0}, Value: {1}", em, ev)
MsgBox(mm)
End Sub
End Class
More info here
Hope this helps.
This, as jmcilhinney pointed out, uses Enums and the Description attribute.
Here is the class
'requires
' Imports System.Reflection
' Imports System.ComponentModel
Public Class ErrorCodes
Public Enum ErrCode 'these are the error codes
'note that the codes should be unique
<Description("Success")> ERROR_SUCCESS = 0
<Description("Error A")> ERROR_A = 1
End Enum
Public Class InfoForErrCode
Public TheDescription As String
Public TheValue As Integer
Public AsString As String
End Class
Public Shared Function Info(TheError As ErrCode) As InfoForErrCode
Dim rv As New InfoForErrCode
rv.TheDescription = GetDescription(TheError)
rv.TheValue = TheError
rv.AsString = TheError.ToString
Return rv
End Function
Private Shared Function GetDescription(TheError As ErrCode) As String
Dim rv As String = ""
Dim fi As FieldInfo = TheError.GetType().GetField(TheError.ToString())
Dim attr() As DescriptionAttribute
attr = DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute),
False), DescriptionAttribute())
If attr.Length > 0 Then
rv = attr(0).Description
Else
rv = TheError.ToString()
End If
Return rv
End Function
End Class
And here is how it can be used
Dim foo As ErrorCodes.ErrCode = ErrorCodes.ErrCode.ERROR_SUCCESS
Dim inf As ErrorCodes.InfoForErrCode = ErrorCodes.Info(foo)
Stop 'examine inf
foo = ErrorCodes.ErrCode.ERROR_A
inf = ErrorCodes.Info(foo)
Stop 'examine inf

VB .Net - ShouldSerialize function

I have a problem with ShouldSerialize function.
I defined an usercontrol with a label (named Label1) on it.
Now i put the code below in the usercontrol class :
Imports System.ComponentModel
Public Class UserControl1
Dim _Range As UShort = 100
Private Sub UserControl1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.Text = _Range
End Sub
Public Function ShouldSerializeTBValueRange() As Boolean
Return _Range <> 200
End Function
Public Sub ResetTBValueRange()
_Range = 200
Label1.Text = _Range
End Sub
Public Property TBValueRange As UShort
Get
Return _Range
End Get
Set(ByVal Steps As UShort)
_Range = Steps
Label1.Text = _Range
End Set
End Property
End Class
Now in a new form include the usercontrol.
In the properties grid of the usercontrol u can find the property TBValueRange.
If you right click in the property name you can Reinit the property.
After reinit u can see value 200 in the property.
Now, regenerate the project will reset the property to the initial value (id 100).
Why the value 200 didn't stay?
If i replace the line Return _Range <> 200 in the function ShouldSerializeTBValueRange() by
Return Not _Range.Equals(200)
it will work.
i don't understand.
Anyone could explain this?
The methods are working as coded. The thing which seems to not work as expected is the Not _Range.Equals(200) portion.
You initialize _Range to 100: Dim _Range As UShort = 100
Then, your ShouldSerializeTBValueRange method tells VS to only save the range value when it is not 200: Return _Range <> 200
So, when you reset it to 200 in the IDE, it wont save a value of 200, and the initial value of 100 displays.
The code uses one value for the default, 100 but use a different value for ShouldSerialize test. You should either change it to use a default of 200:
Dim _Range As UShort = 200
Or change the ShouldSerialize test to use 100:
Public Function ShouldSerializeTBValueRange() As Boolean
Return _Range <> 100
End Function
Treating the default value as the default, and using only one value for the default everything works as expected using just ShouldSerializexxx and Resetxxx with no need for anything else.
' THIS is the default value -
' ie the value that need not be saved because the
' control automatically starts with that value
Dim _Range As UShort = 200US
' Controls:
' - displays the prop value in Bold in the property window
' when the value is NOT the default
' - saves the value when it is NOT the default
' - enables/disables the RESET function
Public Function ShouldSerializeTBValueRange() As Boolean
Return (_Range <> 200US)
End Function
Using one value for the actual default (100) and then returning T/F based on a different value (200) results in
100 being saved when it need not be
200 not being saved when it should be
the Reset menu item and Bold value being in the incorrect state
The oddity is that Not _Range.Equals(200) seems to fail. It returns False after a reset (to 200) which causes the value to be saved when it really should not. There are 2 overloads for this:
UInt16.Equals(obj As Object) As Boolean
UInt16.Equals(v As UShort) As Boolean
If you pass anything other than an actual UInt16/UShort, the value will be boxed as Object. So, Not _Range.Equals(200) is using the first because 200 is Int32. The ILCode for that version is:
public override bool Equals(object obj)
{
return obj is ushort && this == (ushort)obj;
}
The test will fail the first test because obj contains an Int32. If you pass a UShort, it will work:
UShort.Equals(_Range, 200US)
'or:
_Range.Equals(200US)
All good reasons to understand the different data types, avoid boxing (As Object) and always and forever set Option Strict On.
Thanks you all to have spent time on my question.
I found my answer : when ShouldSerialize function return false the property value isn't save in the file Form1.designer.vb, so it gets the original value.
For my needed the ShouldSerialize function should always return true.
Public Function ShouldSerializeTBValueRange() As Boolean
Return true
End Function
So the property value will always be saved.
Now, what i'm looking for is how to control the context menu of the property grid.
I want the option "Reinit" greyed when the value is good.
Finally, i found something, with the use of PropertyDescriptor.
For whose are interested, we can save properties in the designer.vb file and test the value to reset.
here is my program, based on the article from MSDN - Microsft:
Imports System.ComponentModel
Imports System.Windows.Forms.Design
<Designer(GetType(DemoControlDesigner))>
Public Class UserControl1
Dim _MyProperty1 As String = "Test1"
Dim _MyProperty2 As Integer = 100
Dim _MyProperty3 As UShort = 200
Public Sub New()
InitializeComponent()
End Sub
Public Function CanResetMyProperty1() As Boolean
Return _MyProperty1 <> "Test"
End Function
Public Sub ResetMyProperty1()
_MyProperty1 = "Test"
End Sub
Public Property MyProperty1 As String
Get
Return _MyProperty1
End Get
Set(ByVal Value As String)
_MyProperty1 = Value
End Set
End Property
Public Function CanResetMyProperty2() As Boolean
Return _MyProperty2 <> 150
End Function
Public Sub ResetMyProperty2()
_MyProperty2 = 150
End Sub
Public Property MyProperty2 As Integer
Get
Return _MyProperty2
End Get
Set(ByVal Value As Integer)
_MyProperty2 = Value
End Set
End Property
Public Function CanResetMyProperty3() As Boolean
Return _MyProperty3 <> _MyProperty2
End Function
Public Sub ResetMyProperty3()
_MyProperty3 = _MyProperty2
End Sub
Public Property MyProperty3 As UShort
Get
Return _MyProperty3
End Get
Set(ByVal Value As UShort)
_MyProperty3 = Value
End Set
End Property
End Class
Friend NotInheritable Class SerializePropertyDescriptor
Inherits PropertyDescriptor
Private _pd As PropertyDescriptor = Nothing
Public Sub New(ByVal pd As PropertyDescriptor)
MyBase.New(pd)
_pd = pd
End Sub
Public Overrides ReadOnly Property Attributes() As AttributeCollection
Get
Return Me._pd.Attributes
End Get
End Property
Protected Overrides Sub FillAttributes(ByVal attributeList As IList)
MyBase.FillAttributes(attributeList)
End Sub
Public Overrides ReadOnly Property ComponentType() As Type
Get
Return Me._pd.ComponentType
End Get
End Property
Public Overrides ReadOnly Property Converter() As TypeConverter
Get
Return Me._pd.Converter
End Get
End Property
Public Overrides Function GetEditor(ByVal editorBaseType As Type) As Object
Return Me._pd.GetEditor(editorBaseType)
End Function
Public Overrides ReadOnly Property IsReadOnly() As Boolean
Get
Return Me._pd.IsReadOnly
End Get
End Property
Public Overrides ReadOnly Property PropertyType() As Type
Get
Return Me._pd.PropertyType
End Get
End Property
Public Overrides Function CanResetValue(ByVal component As Object) As Boolean
Try
Return CallByName(component, "CanReset" & _pd.Name, CallType.Get, Nothing)
Catch ex As Exception
MsgBox(ex.Message)
End Try
Return False
End Function
Public Overrides Function GetValue(ByVal component As Object) As Object
Return Me._pd.GetValue(component)
End Function
Public Overrides Sub ResetValue(ByVal component As Object)
Me._pd.ResetValue(component)
End Sub
Public Overrides Sub SetValue(ByVal component As Object, ByVal val As Object)
Me._pd.SetValue(component, val)
End Sub
Public Overrides Function ShouldSerializeValue(ByVal component As Object) As Boolean
Return True
End Function
End Class
Class DemoControlDesigner
Inherits ControlDesigner
Dim PropertiesToSerialize As String() = {"MyProperty1", "MyProperty2", "MyProperty3"}
Protected Overrides Sub PostFilterProperties(ByVal properties As IDictionary)
Dim original As PropertyDescriptor
For Each PropName As String In PropertiesToSerialize
If properties.Contains(PropName) Then
original = properties(PropName)
properties(PropName) = New SerializePropertyDescriptor(original)
End If
Next
MyBase.PostFilterProperties(properties)
End Sub
End Class
For each property that should be serialized and tested for reset, we should write a sub "CanResetPropertyName" which test the value to reset (see sample).
And the values stay now, even if we regenerate the project.
It's working fine for me, maybe it can be improved.
Regards.

Sort an arraylist of objects by one of the properties?

Hi I have an object called item here is the code for it:
Public Class item
Private itemValue As String
Private urlfoundValue As String
Private typeValue As String
Private scorevalue As String
Public Sub New()
' Leave fields empty.
End Sub
Public Sub New(ByVal item As String, ByVal urlfound As String, ByVal type As String, ByVal score As String)
itemValue = item
urlfoundValue = urlfound
typeValue = type
scorevalue = score
End Sub
Public Property item() As String
Get
Return itemValue
End Get
Set(ByVal value As String)
itemValue = value
End Set
End Property
Public Property urlfound() As String
Get
Return urlfoundValue
End Get
Set(ByVal value As String)
urlfoundValue = value
End Set
End Property
Public Property type() As String
Get
Return typeValue
End Get
Set(ByVal value As String)
typeValue = value
End Set
End Property
Public Property score() As String
Get
Return scorevalue
End Get
Set(ByVal value As String)
scorevalue = value
End Set
End Property
I have an ArrayList of these items that I would like to sort in highest to lowest order by the Score property (is string but can be converted to int32).
The items are all store in this arraylist:
Public fullitem As New System.Collections.ArrayList()
Any suggestions on how to get the items in order by the score? Can be done in c# too.
A direct answer to your question is to use LINQ like this:
Dim orderdList = fullitem.Cast(Of item)() _
.OrderBy(Function(i As item) Convert.ToInt32(i.score))
fullitem = New System.Collections.ArrayList()
For Each item In orderdList
fullitem.Add(item)
Next
This uses Cast to obtain a generic IEnumerable<item> from the ArrayList which then enables you to use LINQ functions like OrderBy.
However, please note that ArrayList is now obsolete, and you should consider using List<Item> instead. If you do that then you can do something as simple as this:
Assuming you have the list defined like this:
Dim list = new List(Of item)()
You can sort it like this:
list = list.Cast(Of item)() _
.OrderBy(Function(i As item) Convert.ToInt32(i.score)) _
.ToList()

Assigning Private Property values

I'm testing out PetaPoco to determine if it meets our needs. I was under the impression that it would delve into my class and see the private properties that I've marked up as valid columns. Unfortunately, it's not behaving as expected. Can you please take a look and let me know if I'm doing something wrong.
My test class:
Imports PetaPoco
<PetaPoco.TableName("PaulTest")>
<PetaPoco.PrimaryKey("ID")>
Public Class PaulTest
<PetaPoco.Column("ID")>
Private Property pvtID As Integer
Private pvtName As String
Private Sub New()
End Sub
Public Sub New(name As String)
If String.IsNullOrEmpty(name) Then
Throw New ArgumentException("Passed Name is empty")
End If
Me.pvtName = name
End Sub
<PetaPoco.Ignore>
Public ReadOnly Property ID As Integer
Get
Return pvtID
End Get
End Property
<PetaPoco.Column("Name")>
Public Property Name As String
Get
Return pvtName
End Get
Set(value As String)
If String.IsNullOrEmpty(value) Then
Throw New ArgumentException("Passed Name is empty")
End If
Me.pvtName = value
End Set
End Property
End Class
The call to the DB (_db is a PetaPoco database object)
Return (_db.Fetch(Of Peta.Domain.PaulTest)(";EXEC selAllPaulTest")).OrderBy(Function(PaulTest) (PaulTest.ID)).ToList()
What's in the database
ID Name
107 Paul
What's being returned:
PaulTest.ID = 0
PaulTest.pvtID = 0
PaulTest.Name = "Paul"
PaulTest.pvtName = "Paul"
<PetaPoco.Ignore> attribute instructs PetaPoco to ignore the column during mapping. As such, the property Id will always be returning the default value of 0.
If you want to prevent the Id property from being modified, comment our or delete the attribute and add a private setter to the Id property as in the following code snippet.
' <PetaPoco.Ignore>
Public Property Id As Integer
Get
Return pvtID
End Get
Private Set(value As Integer)
pvtID = value
End Set
End Property