Update Dimensional Concurrent Dictionary - vb.net

I have this little function that I found but I am having a hard time trying to call it correctly. how can I call it to update my ConcurrentDictionary
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Animals As New Concurrent.ConcurrentDictionary(Of String, String())
Dim iKey = "key123456"
Animals(iKey) = {"cat", "dog", "bird"}
Dim success As Boolean = TryUpdate(Animals, iKey, Func("cat", "frog"))
End Sub
Function TryUpdate(Of TKey, TValue)(dict As Concurrent.ConcurrentDictionary(Of TKey, TValue), key As TKey, updateFactory As Func(Of TValue, TValue)) As Boolean
Dim curValue As TValue
If Not dict.TryGetValue(key, curValue) Then
Return False
End If
dict.TryUpdate(key, updateFactory(curValue), curValue)
Return True
End Function
End Class

TryUpdate has three parameters,
the ConcurrentDictionary to be updated,
the Key to be updated in the dictionary, and
a delegate function that accepts the key's current value, and returns the desired value.
Dim success As Boolean = TryUpdate(myDictionary, myKey, Func(oldval) newval)
How you pass the third parameter is up to you, but it looks like the intention is so you can have a look at the old value to ensure it is what you expected, then passing either your new value, or the returned value accordingly.
Addition for clarity: The third parameter is expecting to be passed a delegate to a function which will accept the current value of the key you are trying to change, and return a new value (or the original value, if you don't want to change it).
Here I'm creating a function CheckValue that determines if the old value is what I expected, then if so returns the new value. The myDel is a delegate of that function which is passed into TryUpdate.
Dim whatIExpected As String = ""
Dim newVal As String = ""
Dim myDel As Func(Of String, String) = AddressOf CheckValue
Public Function CheckValue(ByVal oldVal As String) As String
If (oldVal = whatIExpected) Then
Return newVal
Else
Return oldVal
End If
End Function
'Then later inside some function or sub..
whatIExpected = "cat"
newVal = "frog"
Dim success As Boolean = TryUpdate(myDictionary, myKey, myDel)

Related

Sort a list based on an item inside the list that is another list

As the titel says, but it gets even more complicated. this is some example code
class person
prop name as string
prop age as int
prop properties as List(of ExtraProps)
class ExtraProps
prop key as string
prop value as string
so say that i want to sort a list of class person based on an object ExtraProps.value where the Key = "name"
do note that i am working in vs2005 and in version 2.0 of .NET
E.g.
Private Function ComparePersonsByExtraPropsName(p1 As Person, p2 As Person) As Integer
Return GetFirstExtraProp(p1.Properties, "Name").Value.CompareTo(GetFirstExtraProp(p2.Properties, "Name").Value)
End Function
Private Function GetFirstExtraProp(properties As IEnumerable(Of ExtraProps), key As String) As ExtraProps
For Each ep As ExtraProps In properties
If ep.Key = key Then
Return ep
End If
Next
Return Nothing
End Function
and then:
Dim personList As New List(Of Person)
'...
personList.Sort(AddressOf ComparePersonsByExtraPropsName)
First i would like to thank #TimSchmelter For this answer. this works perfectly in .NET 2.0 and in VS2005.
Public Class TaskKeyComparer
Implements IComparer(Of Trimble_Planning.TaskData)
Private ReadOnly _keyComparison As StringComparison
Private ReadOnly _valueComparison As StringComparison
Private ReadOnly _key As String
Public Sub New(ByVal key As String, Optional ByVal keyComparison As StringComparison = StringComparison.CurrentCulture, Optional ByVal valueComparison As StringComparison = StringComparison.CurrentCulture)
_key = key
_keyComparison = keyComparison
_valueComparison = valueComparison
End Sub
Public Function Compare(ByVal x As person, ByVal y As person) As Integer Implements IComparer(Of person).Compare
If x Is Nothing AndAlso y Is Nothing Then Return 0
If x Is Nothing OrElse y Is Nothing Then Return CInt(IIf(x Is Nothing, -1, 1))
If x.properties Is Nothing AndAlso y.properties Is Nothing Then Return 0
If x.properties Is Nothing OrElse y.properties Is Nothing Then Return CInt(IIf(x.properties Is Nothing, -1, 1))
Dim xKeyValue As String = Nothing
Dim yKeyValue As String = Nothing
For Each prop As ExtraProps In x.properties
If String.Equals(prop.key, _key, _keyComparison) Then
xKeyValue = prop.value
Exit For
End If
Next
For Each prop As ExtraProps In y.properties
If String.Equals(prop.key, _key, _keyComparison) Then
yKeyValue = prop.value
Exit For
End If
Next
Return String.Compare(xKeyValue, yKeyValue, _valueComparison)
End Function
End Class
then use it like this
personList.Sort(New TaskKeyComparer("name"))

How do I reference a DIM Name (not value) in a function?

I have a Dim publicly declared in my Form, how would I set/change the value of this Dim in a function without manually calling it like this: Test = "New Val"?
I would need it to be something like this:
Public Class Form2
Dim Test As String = "I do not want to read this value in the function,
I do however need to change this value"
Private Function PassingDimName(ByVal DimName As dim)
'Having it like this gives the following error:
'"Keyword does not name a type"
DimName = "Dims new value"
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PassingDimName(Test)
End Sub
End Class
Use System.Reflection.GetField (String, BindingFlags) to access a field by name
Here are some functions for setting and getting field values:
Public Sub SetValue(name As String, value As Object)
Dim fi As FieldInfo = Me.GetType().GetField(name, BindingFlags.NonPublic Or BindingFlags.Instance)
fi.SetValue(Me, value)
End Sub
Public Function GetValue(name As String) As Object
Dim fi As FieldInfo = Me.GetType().GetField(name, BindingFlags.NonPublic Or BindingFlags.Instance)
Return fi.GetValue(Me)
End Function
Note: the BindingFlags are specific to your application, where you have declared a private field (Dim Test As ...). See BindingFlags Enumeration if you want to use something other than a private field.
In your specific implementation, you could set a new string to the field in the button click like this
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SetValue("Test1", "new value")
End Sub
A minimal, complete example to demonstrate how it works could look like this
Dim f2 = New Form2()
Console.WriteLine(f2.GetValue("Test1"))
Console.WriteLine(f2.GetValue("Test2"))
f2.SetValue("Test1", "new value")
f2.SetValue("Test2", 1)
Console.WriteLine(f2.GetValue("Test1"))
Console.WriteLine(f2.GetValue("Test2"))
with the form
Public Class Form2
Dim Test1 As String = "initial value"
Dim Test2 As Integer = 0
Public Sub SetValue(name As String, value As Object)
Dim fi As FieldInfo = Me.GetType().GetField(name, BindingFlags.NonPublic Or BindingFlags.Instance)
fi.SetValue(Me, value)
End Sub
Public Function GetValue(name As String) As Object
Dim fi As FieldInfo = Me.GetType().GetField(name, BindingFlags.NonPublic Or BindingFlags.Instance)
Return fi.GetValue(Me)
End Function
End Class
output:
initial value
0
new value
1
Another note: when setting the field, you aren't guaranteed to know the type. Trying f2.SetValue("Test2", "new value") would compile but would fail at runtime when trying to convert a string to an integer. If you know all your fields are strings, then it may be better to declare Public Sub SetValue(name As String, value As String) and Public Function GetValue(name As String) As String with the appropriate casts.
If you just want to change the value of the variable then change your function to this.
Private Sub PassingDimName(ByRef DimName As String)
'Having it like this gives the following error:
'"Keyword does not name a type"
DimName = "Dims new value"
End Sub

Getting Installed Steam Games From Registry

Can someone help me with this error I'm getting? Error14 'Using' operand of type 'System.Collections.Generic.List(Of String)' must implement 'System.IDisposable'
Public Function GetInstalledGames() As Object
Dim enumerator As IEnumerator(Of String) = Nothing
Dim list As List(Of String) = Directory.GetFiles(String.Concat(Me.SteamPath, "\steamapps")).ToList()
Using strs As List(Of String) = New List(Of String)()
enumerator = list.Distinct().GetEnumerator()
While enumerator.MoveNext()
Dim current As String = enumerator.Current
If (current.Contains("appmanifest_") And current.Contains(".acf")) Then
strs.Add(Path.GetFileName(current).Replace("appmanifest_", "").Replace(".acf", ""))
End If
End While
End Using
Return strs
End Function
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
Dim enumerator As IEnumerator(Of String) = Nothing
Me.tbOutput.Text = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
Me.SteamPath = Conversions.ToString(Me.GetSteamPath())
Using installedGames As List(Of String) = DirectCast(Me.GetInstalledGames(), List(Of String))
enumerator = installedGames.Distinct().GetEnumerator()
While enumerator.MoveNext()
Dim current As String = enumerator.Current
Me.lbGames.Items.Add(current)
End While
End Using
End Sub
Stop writing explicit enumerator loops for no reason
Make your functions return types that make sense instead of Object
Don’t sprinkle Using into code without knowing what it does
Pass data between functions through arguments, not class fields
Private Shared Function GetInstalledGames(steamPath As String) As IEnumerable(Of String)
Dim result As New List(Of String)
For Each name In Directory.GetFiles(Path.Combine(steamPath, "steamapps"))
If name.Contains("appmanifest_") AndAlso name.Contains(".acf") Then
result.Add(Path.GetFileNameWithoutExtension(name).Replace("appmanifest_", ""))
End If
Next
Return result
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs)
Me.tbOutput.Text = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
Dim steamPath As String = Me.GetSteamPath()
For Each current In GetInstalledGames(steamPath).Distinct()
Me.lbGames.Items.Add(current)
Next
End Sub

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

Extracting property values from a dictionary

I am attempting to write a subroutine that will deserialize a dictionary from a .ser file (this bit works fine) and then repopulate several lists from this dictionary (this is the bit I cannot do).
The dictionary contains objects (I think) of a custom class I wrote called "Photo Job" which has properties such as ETA, notes, medium etc. (Declared as such)
Dim photoJobs As New Dictionary(Of String, PhotoJob)
In short, I want to be able to extract every entry of each specific property into an separate arrays (one for each property) and I can go from there.
Any help would be appreciated, I may be going about this completely the wrong way, I'm new to VB. The relevant code is below:
Photo Job Class:
<Serializable()> _Public Class PhotoJob
Private intStage As Integer 'Declare all local private variables
Private ID As String
Private timeLeft As Integer
Private material As String '
Private note As String
Private path As String
Private finished As Boolean = False
'Declare and define properties and methods of the class
Public Property productionStage() As Integer
Get
Return intStage
End Get
Set(ByVal Value As Integer)
intStage = Value
End Set
End Property
Public Property photoID() As String
Get
Return ID
End Get
Set(ByVal Value As String)
ID = Value
End Set
End Property
Public Property ETA() As Integer
Get
Return timeLeft
End Get
Set(ByVal Value As Integer)
timeLeft = Value
End Set
End Property
Public Property medium() As String
Get
Return material
End Get
Set(ByVal Value As String)
material = Value
End Set
End Property
Public Property notes() As String
Get
Return note
End Get
Set(ByVal Value As String)
note = Value
End Set
End Property
Public Property imagePath() As String
Get
Return path
End Get
Set(ByVal Value As String)
path = Value
End Set
End Property
Public Property complete() As Boolean
Get
Return finished
End Get
Set(value As Boolean)
finished = value
End Set
End Property
Public Sub nextStage()
If intStage < 4 Then
intStage += 1
ElseIf intStage = 4 Then
intStage += 1
finished = True
End If
End Sub
End Class
Subroutines involved in de/serialisation:
Private Sub BackupAllToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles BackupAllToolStripMenuItem.Click
Dim formatter As New BinaryFormatter
Dim backupFile As New FileStream(Strings.Replace(Strings.Replace(Now, ":", "_"), "/", ".") & ".ser", FileMode.Create, FileAccess.Write, FileShare.None)
formatter.Serialize(backupFile, photoJobs)
backupFile.Close()
MsgBox("Collection saved to file")
End Sub
Private Sub RestoreFromFileToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles RestoreFromFileToolStripMenuItem.Click
With OpenFileDialog 'Executes the following sets/gets/methods of the OpenFileDialog
.FileName = ""
.Title = "Open Image File"
.InitialDirectory = "c:\"
.Filter = "Serial Files(*.ser)|*ser"
.ShowDialog()
End With
Dim backupPathStr As String = OpenFileDialog.FileName
Dim deSerializer As New BinaryFormatter
Dim backupFile As New FileStream(backupPathStr, FileMode.Open)
photoJobs = deSerializer.Deserialize(backupFile)
backupFile.Close()
End Sub
From what I can see using the autos menu, the saving/restoring of the dictionary works just fine.
First, if you are using VS2010+, you can greatly reduce boilerplate code using autoimplemented properties:
<Serializable()>
Public Class PhotoJob
Public Property productionStage() As Integer
Public Property photoID() As String
Public Property ETA() As Integer
etc
End Class
That is all that is needed, all the boilerplate code is handled for you. Second, with this line:
photoJobs = deSerializer.Deserialize(backupFile)
Your deserialized photojobs will be a generic Object, not a Dictionary. You should turn on Option Strict so VS will enforce these kinds of errors. This is how to deserialize to Type:
Using fs As New FileStream(myFileName, FileMode.Open)
Dim bf As New BinaryFormatter
PhotoJobs= CType(bf.Deserialize(fs), Dictionary(Of String, PhotoJob))
End Using
Using closes and disposes of the stream, CType converts the Object returned by BF to an actual dictionary
To work with the Dictionary (this has nothing to do with Serialization) you need to iterate the collection to get at the data:
For Each kvp As KeyValuePair(Of String, PhotoJob) In PhotoJobs
listbox1.items.Add(kvp.value.productionStage)
listbox2.items.Add(kvp.value.ETA)
etc
Next
The collection is a made of (String, PhotoJob) pairs as in your declaration, and when you add them to the collection. They comeback the same way. kvp.Key will be the string key used to identify this job in the Dictionary, kvp.Value will be a reference to a PhotoJobs object.
As long as VS/VB knows it is a Dictionary(of String, PhotoJob), kvp.Value will act like an instance of PhotoJob (which it is).