How to use ConcurrentDictionary(of Integer, Class).TryUpdate? - vb.net

What is the correct way to update a ConcurrentDictionary with a new value? I am trying AllWidgets.TryUpdate(id, myWidget, myWidget) and it returns false and does not update correctly in this type of scenario:
Public Class Widget
Public ID As Integer
Public Name As String
Public Sub New(ByVal id As Integer, ByVal name As String)
ID = id
Name = name
End Sub
End Class
Dim AllWidgets As New ConcurrentDictionary(Of Integer, Widget)
AllWidgets.TryAdd(1, New Widget(1000, "Widget A"))
AllWidgets.TryAdd(2, New Widget(1001, "Widget B"))
Dim UpdateWidget As New Widget(1001, "Widget BB")
Dim IsUpdated As Boolean = AllWidgets.TryUpdate(2, UpdateWidget, UpdateWidget)
IsUpdated is False
I guess I really don't understand how the third parameter is supposed to work for complex objects.

You'll never get True this way. The first thing you have to do is make Widgets comparable, override GetHashCode() and Equals(). Like this:
Public Class Widget
''...
Public Overrides Function GetHashCode() As Integer
Return Me.ID.GetHashCode() Xor Me.Name.GetHashCode()
End Function
Public Overrides Function Equals(obj As Object) As Boolean
Dim w = CType(obj, Widget)
Return w.ID = Me.ID AndAlso w.Name = Me.Name
End Function
End Class
Now ConcurrentDictionary can compare widgets. You'll get a True return this way:
Dim UpdateWidget As New Widget(1001, "Widget BB")
Dim OldWidget As New Widget(1001, "Widget B")
Dim IsUpdated As Boolean = AllWidgets.TryUpdate(2, UpdateWidget, OldWidget)
Debug.Assert(IsUpdated) '' fine

Related

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

Comparing list of class by string length and text comparison

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

VB.NET Object with custom name to store property?

I'm not familiar with the type of structure or whatever I need to use to achieve this, but I know that there is one.
I'm trying to make it so that I can reference things something like this:
racerlist(x).compatibilityArr.john.CleatScore
instead of what I have to do now:
racerlist(x).compatibilityArr.CleatScoreArr(y).name/.score
So essentially, I want to add items to the compatibilityarr (will probably have to change to a list which is fine) and be able to reference the racer as their own name, instead of by using an index.
This is one way to build a solution that fits your needs as described above. It requires an embedded class that is built as a List(Of T) where we overload the property to accept a string rather than the integer.
Public Class Foo
Public Property compatibilityArr As New Members
End Class
Public Class Members : Inherits List(Of Member)
Public Overloads ReadOnly Property Item(name As String) As Member
Get
Return Me.Where(Function(i) i.Name = name).FirstOrDefault
End Get
End Property
End Class
Public Class Member
Public Property Name As String
Public Property CleatScore As Integer
End Class
Then to use it:
Public Class Form1
Dim f As New Foo
Private Sub loads() Handles Me.Load
Dim member As New Member With {.Name = "John", .CleatScore = 10}
f.compatibilityArr.Add(member)
MessageBox.Show(f.compatibilityArr.Item("John").CleatScore)
End Sub
End Class
There are other ways to do this, but the simplest is to write a function to search the array by name:
Sub Main1()
Dim racerlist(2) As Racer
racerlist(0) = New Racer With {.Name = "Adam", .CleatScore = "1"}
racerlist(1) = New Racer With {.Name = "Bill", .CleatScore = "2"}
racerlist(2) = New Racer With {.Name = "Charlie", .CleatScore = "3"}
For i As Integer = 0 To racerlist.GetUpperBound(0)
For j As Integer = 0 To racerlist.GetUpperBound(0)
If racerlist(j).Name <> racerlist(i).Name Then
ReDim Preserve racerlist(i).CompatibilityArr(racerlist(i).CompatibilityArr.GetUpperBound(0) + 1)
racerlist(i).CompatibilityArr(racerlist(i).CompatibilityArr.GetUpperBound(0)) = racerlist(j)
End If
Next j
Next i
Dim racerBill As Racer = Racer.FindRacer(racerlist, "Bill")
MsgBox(racerBill.FindCompatibility("Charlie").CleatScore)
End Sub
Class Racer
Property Name As String
Property CleatScore As String
Property CompatibilityArr As Racer()
Sub New()
ReDim CompatibilityArr(-1) 'initialise the array
End Sub
Function FindCompatibility(name As String) As Racer
Return FindRacer(CompatibilityArr, name)
End Function
Shared Function FindRacer(racerlist() As Racer, name As String) As Racer
For i As Integer = 0 To racerlist.GetUpperBound(0)
If racerlist(i).Name = name Then
Return racerlist(i)
End If
Next i
Return Nothing
End Function
End Class
As #Codexer mentioned, I used a dictionary to achieve this.
In my list of Racers (RacerList), I have RacerCompatibility, which I created similar to below:
Public RacerCompatibility As New Dictionary(Of String, Compatibility)
Compatibility is created like:
Public Class Compatibility
Public Cleat As Boolean
Public Skill As Integer
Public Height As Integer
End Class
So now I can access the compatibility of a racer inside the list like:
RacerList(x).RacerCompatibility.Item("John")

Create Custom Class Dynamically

I am working on a project where I need to create a multitude of custom classes to interact properly with an API (While I know there might be questions on why, and such, but the short is it has to be this way).
Is there a way to create a complete custom class dynamically on the fly? So instead of
class person
Private _Height
Property Height As Integer
Get
Return _Height
End Get
Set(value As Integer)
_Height = value
End Set
End Property
'Continue for all properties of person
I would like to be able to create a new object and through other input create this dynamically.
dim NewClass as object
dim NewProperty as property
NewProperty.name="Height"
NewProperty.datatype=string
NewClass.AddProperty(NewProperty)
Is this possible? It would save me a lot of time if it is.
I don't like late binding but there are options (I like my option strict on). Like using the DynamicObject or the ExpandoObject class. Your question is vague so I have no idea if it can work.
Sub Main()
Dim test As Object = New SampleDynamicClass()
test.SomeProperty = "123"
Console.WriteLine(test.SomeProperty)
Console.ReadLine()
End Sub
Public Class SampleDynamicClass
Inherits DynamicObject
Private _values As New Dictionary(Of String, String)
Public Sub New()
End Sub
Public Function GetPropertyValue(ByVal propertyName As String) As String
Return _values(propertyName)
End Function
Public Function SetPropertyValue(ByVal propertyName As String, ByVal value As Object) As Boolean
If _values.ContainsKey(propertyName) Then
_values(propertyName) = value.ToString()
Else
_values.Add(propertyName, value.ToString())
End If
Return True
End Function
Public Overrides Function TryGetMember(ByVal binder As GetMemberBinder,
ByRef result As Object) As Boolean
result = GetPropertyValue(binder.Name)
Return If(result Is Nothing, False, True)
End Function
Public Overrides Function TryInvokeMember(ByVal binder As InvokeMemberBinder,
ByVal args() As Object,
ByRef result As Object) As Boolean
result = GetPropertyValue(binder.Name)
Return If(result Is Nothing, False, True)
End Function
Public Overrides Function TrySetMember(binder As SetMemberBinder, value As Object) As Boolean
Return SetPropertyValue(binder.Name, value)
End Function
Dim person = New With {Key .Height = 12}
Dim personTypes = New With {Key .Happy = 1, .Sad = 2}
Dim personsAndTypes = New With {Key .Person = person, .Type = personTypes}
The question is kind of vague, but if you have no need for other fields and methods, or reuse Anonymous Types

How to get table record count by passing table name as parameter using LINQ TO SQL

I have a method called getTableRecordCount.
Public Function getTableRecordCount(ByVal strTableName As String, ByVal iParam1 As Integer, iParam2 As Integer) As Boolean
Try
Dim detailReturned = (From paramTableName In dc.<need to pass the strTableName here>
Where paramTableName.col1 = iParam1 And paramTableName.col2 = iParam2
Select paramTableName).Count
If (detailReturned > 0) Then
Return True
Else
Return False
End If
Catch ex As Exception
.....
End Try
End Function
If I can pass the table name and have the DataContext I can use the same method for getting other tables
record count. Any input on how to achieve this?
You could create a method where you pass a data context and expression.
Then from data context (based on Linq2Sql as you did not specify framework) you can get table by using
GetTable<T>()
method on your data context.
Expression would be your parameters.
Hope it helps.
Hopefully my VB is okay; I'm more comfortable in C#. I'm not sure if this would work with LINQ to SQL, but the GetTableRecordCount test near the bottom does pass. I'm assuming you're working with different data types in your tables, hence the generic method.
Imports System.Text
Imports System.Linq.Expressions
Class Product
Public Id As Integer
Public Name As String
Sub New(id As Integer, name As String)
Me.Id = id
Me.Name = name
End Sub
End Class
Class Order
Public Id As Integer
Public NumberOfItems As Integer
Sub New(id As Integer, numItems As String)
Me.Id = id
Me.NumberOfItems = numItems
End Sub
End Class
Class DataContext
Public Property Products As IEnumerable(Of Product)
Public Property Orders As IEnumerable(Of Order)
Sub New()
Me.Products = New Product() {New Product(1, "Apple"), New Product(2, "Banana")}
Me.Orders = New Order() {New Order(1, 20), New Order(2, 50)}
End Sub
End Class
<TestClass()>
Public Class Main
Dim MyDataContext As DataContext
<TestMethod()>
Public Sub GetTableRecordCount()
Me.MyDataContext = New DataContext()
Assert.IsTrue(Me.GetTableRecordCount(Of Product)("Products", Function(p) p.Id, 1, Function(p) p.Name.Length, 5))
Assert.IsTrue(Me.GetTableRecordCount(Of Order)("Orders", Function(o) o.Id, 2, Function(o) o.NumberOfItems, 50))
End Sub
Private Function GetTableRecordCount(Of TRow)(tableName As String, getParam1 As Func(Of TRow, Integer), iParam1 As Integer, getParam2 As Func(Of TRow, Integer), iParam2 As Integer) As Boolean
Try
Dim propertyExpr = Expression.Property(Expression.Constant(Me.MyDataContext), tableName)
Dim getTableData = Expression.Lambda(Of Func(Of IEnumerable(Of TRow)))(propertyExpr).Compile()
Dim detailReturned =
From row In getTableData()
Where getParam1(row) = iParam1 And getParam2(row) = iParam2
Select row
Return detailReturned.Count() > 0
Catch ex As Exception
Return False
End Try
End Function
End Class