Need a Syncroot for a LinkedList(of T) - vb.net

I am using VB.Net and would like to use a LinkedList. Only problem is that it is a multithreaded application. I saw from MSDN that Syncroot is an explicit implementation of the ICollection Interface. I found people wanting to do similar things with List(Of T). It seems that the solution there is to cast the list to the interface.
I have tried to do what I would imagine to be a similar thing in VB.Net, basically:
Dim TestLinkedList = New LinkedList(Of Long)
SyncLock (Ctype(TestLinkedList, ICollection)).SyncRoot
.
.
.
End SyncLock
Is the above correct?

It will work, that's about all that can be said for it. SyncRoot was a mistake from .NET 1.1, there's no reason to continue the practice.
Dim list = New LinkedList(Of Long)
Dim listLock = New Object
...
SyncLock(listLock)
...
End SyncLock

ICollection.SyncRoot is generally considered a bad idea. You should either lock the collection itself, or lock on a separate lock object you create

Related

vb.net Source array type cannot be assigned to destination array type on Enum

I've had to update a vb.net project from .NetFramework 4 to .NetFramework 4.7.2. In the process the following code is now throwing an error
Dim actuatorModelsArr = DirectCast(retNumberList, Array) Dim
Dim actuatorModels = actuatorModelsArr.Cast(Of ACTUATORMODELS)().ToList()
The error is System.ArrayTypeMismatchException: Source array type cannot be assigned to destination array type.
retNumberList is an Integer array
ACTUATORMODELS is an Enum
in the .netFramework 4 version actuatorModels is a list of the Enum
{System.Collections.Generic.List`1[FisherIECLib.ACTUATORMODELS]}
The list is used later in the module via linq to grab one of the Enums as a return value.
Is there a way around this or a way to create a list of the Enum?
Thanks in advance,
Hank
I think it's interesting that it stopped working. You can replace the code with a Select and cast to make it work
Dim retNumberList = {1, 2, 3, 4}
' either of these will produce a List of your Enum
Dim a = retNumberList.Select(Function(i) DirectCast(i, ACTUATORMODELS)).ToList()
Dim b = retNumberList.Select(Function(i) CType(i, ACTUATORMODELS)).ToList()
As for the original not working:
Dim c = retNumberList.Cast(Of ACTUATORMODELS)().ToList()
The literature suggests this is the equivalent of a (type)obj c# style cast, but both versions of vb.net cast work in the select. I am not sure why.
djv's answer helps fix your problem. Hopefully this answer will explain what's going wrong. If you look at the reference source for Cast(Of T) on an untyped IEnumerable, you'll find that the first thing that happens is the C# equivalent of this:
Dim asTyped = TryCast(source, IEnumerable(Of TResult))
If asTyped IsNot Nothing Then Return asTyped
Surprisingly, this cast will work for Integer() to IEnumerable(Of ACTUATORMODELS). This sets the issue in motion, because when it comes to making a List(Of T) out of the resulting sequence, it turns out that Integer() and ACTUATORMODELS() are actually not interchangeable, but Cast has already treated the sequence as though they are.
Based on some testing, this issue seems to arise out of the interaction of this corner case with a corner case in how the List(Of T) range constructor works and the more general corner case of Integer() vs TEnum().
In the general case of iterating over Integer() as if it were IEnumerable(Of TEnum), it works. You can make a For Each loop over the sequence, the enumeration variable will have TEnum as the type, and you'll see the values as if they were TEnum.
The problem comes in the range constructor for List(Of T), and an optimization there for a source that implements ICollection(Of T). In that case, the ctor will try to use ICollection(Of T).CopyTo to copy the items into the list's internal storage. This is where the error ultimately occurs, because (going back to the implementation of Cast(Of T)) the source is still the Integer() array, and Array.Copy (via Array.CopyTo) is not OK with trying to do that with a destination of TEnum().
I feel like this is a bug somewhere, though I'm not sure if it's in Cast(Of T), the List(Of T) range ctor, or in the array copy handling. I'm also not sure it's something that will get fixed, since it's a corner case that hits what look like significant optimizations and would require a very specialized check to catch.

List contains duplicate Persons

Please see the code below:
Public Function ExecuteDynamicQuery(ByVal strSQL As String, ByVal list As List(Of clsType), ByVal tyType As clsType) As List(Of clsType) Implements IGenie.ExecuteDynamicQuery
Dim objParameterValues As New clsParameterValues
Dim iConnectionBLL As iConnectionBLL
Dim objCon As DbConnection
Dim objDR As DbDataReader
Dim paramValues() As DbParameter
objParameterValues = New clsParameterValues
iConnectionBLL = New clsConnectionBLL()
objCon = iConnectionBLL.getDatabaseTypeByDescription("Genie2")
Using objCon
paramValues = objParameterValues.getParameterValues
objDR = clsDatabaseHelper.ExecuteReader(objCon, CommandType.Text, strSQL, paramValues)
Do While objDR.Read
Dim tyType2 As clsType = tyType
tyType.PopulateDataReader(objDR)
list.Add(tyType2)
Loop
objDR.Close()
Return list
End Using
End Function
An SQL statement is passed to the function along with clsType (the base type). A list of types is returned e.g. a list of Persons. For example, in this case strSQL = "SELECT * FROM Persons". A list of 500 persons is returned but they are all the same person (the last person added to the list). I realise this is because the list is referncing the same object for each entry. How do I change this?
This is a situation where making the method generic would be useful. For instance:
Public Function MyGenericMethod(Of T As New)() As List(Of T)
Dim results As New List(Of T)()
For i As Integer = 0 To 9
Dim item As New T()
' Populate item ...
results.Add(item)
Next
Return results
End Function
For what it's worth, though, I see people trying do this kind of thing often, and it never sits well with me. I'm always the first one in line to suggest that common code should be encapsulated and not duplicated all over the place, but, I've never been convinced that creating some sort of data access layer that encapsulates the calls to ADO, but doesn't also encapsulate the SQL, is a good idea.
Consider for a moment that ADO, is in-and-of-itself an encapsulation of that part of the data-access layer. Sure, it can take a few more lines of code than you might like to execute a simple SQL command, but that extra complexity is there for a reason. It's necessary in order to support all of the features of the data source. If you try to simplify it, inevitably, you will one day need to use some other feature of the data source, but it won't be supported by your simplified interface. In my opinion, each data access method should use all of the necessary ADO objects directly rather than trying to some how create some common methods to do that. Yes, that does mean that many of your data access methods will be very similar in structure, but I think you'll be happier in the long run.
I've reduced your original code. The following sample is functionally equivalent to what you posted. Without knowing more about your types, it will hard to give you anything more than this, but maybe the reduction will make the code clear enough for you to spot a solution:
Public Function ExecuteDynamicQuery(ByVal sql As String, ByVal list As List(Of clsType), ByVal type As clsType) As List(Of clsType) Implements IGenie.ExecuteDynamicQuery
Dim paramValues() As DbParameter = New clsParameterValues().getParameterValues()
Using conn As DbConnection = iConnectionBLL.getDatabaseTypeByDescription("Genie2"), _
rdr As DbDataReader = clsDatabaseHelper.ExecuteReader(conn, CommandType.Text, sql, paramValues)
While rdr.Read()
type.PopulateDataReader(rdr)
list.Add(type)
End While
Return list
End Using
End Function
There are a few additional bits of advice I can give you:
You must have some way to accept parameter information for your query that is separate from the query itself. The ExecuteReader() method that you call supports this, but you only ever pass it an empty array. Fix this, or you will get hacked.
A implementation that uses Generics (as posted in another answer) would be much simpler and cleaner. The Genie interface you're relying doesn't seem to be adding much value. You'll likely do better starting over with a system that understands generics.
The problem of re-using the same object over and over can be fixed by creating a new object inside the loop. As written, the only way to do that is with a New clsType (and it seems you may have Option Strict Off, such that this could blow up on you at run time), through some messy reflection code, a switch to using generics as suggested in #2, or a by accepting a Func(Of clsType) delegate that can build the new object for you.

Should I always lock static methods?

In the following example GetList returns an instance of a static (shared) variable. That one needs locking in order to be thread-safe.
But what about DoSomething which doesn't use any static variables outside the method? Does it need locking too?
EDIT: I'd like to clarify that in this particular case I expect DoSomething to always print 0-100 in sequence (i.e. no 0123745...) regardless of number of calling threads. Or, in general, that different threads don't affect each other's variables (printing to console is only an example). The language in the following example is VB.NET.
As paxdiablo said:
In this case, it appears the only thing touched is the local variable
i which would have a separate copy for every function invocation.
In other words, it wouldn't need protecting.
That is exactly what I was trying to solve. Thank you!
Public Class TestClass
Private Shared lock As New Object
Private Shared list As List(Of Integer)
Public Shared Function GetList() As List(Of Integer)
SyncLock lock
If list Is Nothing Then
list = New List(Of Integer)
End If
Return list
End SyncLock
End Function
Public Shared Sub DoSomething()
Dim i As Integer
For i = 0 To 100
Console.WriteLine(i.ToString)
Next
End Sub
End Class
Well, that would mostly depend on the language which you haven't specified but, generally, if code doesn't touch a resource that another thread can also touch, it doesn't have to be protected.
In this case, it appears the only thing touched is the local variable i which would have a separate copy for every function invocation. In other words, it wouldn't need protecting.
Of course, it could be argued that the console is also a resource and may need protection if, for example, you didn't want lines interfering with each other (synclock the write) or wanted the entire hundred lines output as a single unit (synclock the entire for loop).
But that won't really protect the console, just the block of code here that uses it. Other threads will still be able to write to the console by not using this method.
Bottom line, I don't think you need a synclock in the second method.
This section below is not relevant if you're using SyncLock in VB.Net (as now seems to be the case). The language guarantees that the lock is released no matter how you leave the block. I'll leave it in for hysterical purposes.
I'd be a little concerned about your synclock placement in the first method, especially if the return statement was a transfer of control back to the caller (and the synclock didn't automatically unlock on scope change). This looks like you can return without unlocking, which would be a disaster. I would think the following would be more suitable:
Public Shared Function GetList() As List(Of Integer)
SyncLock lock
If list Is Nothing Then
list = New List(Of Integer)
End If
End SyncLock
Return list
End Function
In general, no.
You need to be clear on the reason why GetList has the locking applied. It's not, as you imply in your first sentence, because it's returning a static variable. You could remove the locking code, and GetList would still be thread safe. But, with the locking, there's an additional guarantee - that the list will only be created once, and all callers of this code will receive a reference to the same list.
Why not avoid the lock altogether and just do this:
Public Class TestClass
Private Shared lock As New Object
Private Shared list As New List(Of Integer)
Public Shared Function GetList() As List(Of Integer)
Return list
End Function
Public Shared Sub DoSomething()
Dim i As Integer
For i = 0 To 100
Console.WriteLine(i.ToString)
Next
End Sub
End Class

Opposite to a Structure?

I'm sure there is an answer to this somewhere but I'm clearly using the wrong terminology in my searches, so I apologise in advance for this inevitably being a duplicate.
Take the function CType. Clearly I can cast (or at least try) a given object to a given reference type. The function will not work if trying to cast to a structure, i.e.
CType(myObject, Integer)
...will generate a compiler error. This I'm sure most often crops up when working with generics:
Public Function GetResults(Of T)() As T
Dim instance As T
Return CType(GetData(instance), T)
End Function
Public Function GetData(ByVal param As myClass) As myClass
'do stuff
Return param
End Function
Public Function GetData(byval param As Integer) As Integer
'do stuff
Return param
End Function
Public Function GetResults(ByVal param As Object) As Object
Throw New NotImplementedException
End Function
Probably not the best of examples but hopefully shows what I mean. The following will work:
Dim result = GetResult(Of myClass)
The following will fail on the CType
Dim result = GetResult(Of Integer)
I'm not a big fan of using exceptions/Try-Catch if logic can be applied, so although I could Try-Catch the CType, I'd rather find a way to route it through different code. I know with Generics I can use
Public Function GetResults(Of T As Structure) As T
What is the opposite? I want only reference types so that CType doesn't fail... I can't overload the T As Structure with a plain T because it considers them identical signatures, so surely there's a keyword I'm missing somewhere? ^^
Long question, probably only requries a one word answer (sorry)... Thanks in advance!
Public Function GetResults(Of T As Class) As T

Action(Of T) in Visual Basic in List(Of T).ForEach

I have searched high and low for documentation on how to use this feature. While the loop I could write would be simple and take no time, I really would like to learn how to use this.
Basically I have a class, say, Widget, with a Save() sub that returns nothing. So:
Dim w as New Widget()
w.Save()
basically saves the widget. Now let's say I have a generic collection List(Of Widget) name widgetList(Of Widget) and I want to run a Save() on each item in that list. It says I can do a
widgetList.ForEach([enter Action(Of T) here])
....but how in the F does this work??? There is no documentation anywhere on the intrablags. Help would be much much appreciated.
well, I'm really outdated now... :-) but in VB it's:
widgetList.ForEach(Sub(w) w.Save())
or, more complicated:
widgetList.ForEach(New Action(Of Widged)(Sub(w As Widged) w.Save()))
If you're using VB9 (VS2008) I don't think you'll be able to use an anonymous function easily - as far as I'm aware, anonymous functions in VB9 have to be real functions (i.e. they have to return a value) whereas Action<T> doesn't return anything. C# 2's anonymous methods and C# 3's lambda expressions are more general, which is why you'll see loads of examples using List<T>.ForEach from C# and very few using VB :(
You could potentially write a MakeAction wrapper which takes a Function<T,TResult> and returns an Action<T>, but I suspect other restrictions on VB9 anonymous functions would make this impractical.
The good news is that VB10 has much more anonymous function support. (C#4 and VB10 are gaining each other's features - I believe MS is trying to go for language parity from now on, to a larger extent than before.)
Until then, to use List<T>.ForEach you'll need to write an appropriate Sub and use AddressOf to create a delegate from it. Here's a small example:
Imports System
Imports System.Collections.Generic
Public Class Test
Shared Sub Main()
Dim names as New List(Of String)
names.Add("Jon")
names.Add("Holly")
names.ForEach(AddressOf PrintMe)
End Sub
Shared Sub PrintMe(ByVal text as String)
Console.WriteLine(text)
End Sub
End Class
new Action(Of T)(AddressOf Widget.Save)
is OK if Widget got a public function called Save.
All others comment are false when I try it.
Assuming that VB does not support lambda expressions, you can create an instance of the Action(of T) delegate in VB using this syntax:
new Action(Of T)(AddressOf Widget.Save)
The below should work although I'm not up to speed on VB.Net so you may need to adjust accordingly.
widgetList.ForEach(w => w.Save())