0

I hope this makes sense.

I have a program that I am converting from a combination of VB6, and VB.Net 2 to C# 4.

I am having a problem with collections in VB, they are a KeyValuePair. I have three collections:

m_oCol(string, clsFeatureObjectCollection) which contains clsFeatureObject<br />
m_oCol(string, clsFeatureObject) which contains clsFeatureCollection<br />
m_oCol(string, clsFeatureCollection) which contains clsFeature

In C# I have converted the m_oCol(string, clsFeatureCollection) which contains clsFeature to a list because I need clsFeature to hold Features inserted in a particular order for accurate processing ie Keys could be 1, 3, 2, 4, 5.

The others I have converted to a dictionary but I am not sure this is correct, however I am not sure what else to use.

Should these all be dictionaries because I am currently getting a cast error when I access a collection that has all three collections in one collection.

I guess what I am asking is should these collections be all the same type? List? Dictionary? or something else? The key factor is the clsFeature must be inserted in a particular order, again ie Keys could be 1, 3, 2, 4, 5. It seems I can only get it in a list to hold the order

Any advice is appreciated

clsFeatureCollection.vb

Option Strict Off
Option Explicit On

Imports ESRI.ArcGIS.esriSystem

Public Class clsFeature

Private m_OID As Integer
Private m_Geometry As ESRI.ArcGIS.Geometry.IGeometry

Public Sub New(ByRef iOID As Integer, ByRef pGeometry As ESRI.ArcGIS.Geometry.IGeometry)
    m_OID = iOID
    m_Geometry = pGeometry
End Sub

Public ReadOnly Property OID() As Integer
    Get
        OID = m_OID
    End Get
End Property

Public ReadOnly Property Geometry() As ESRI.ArcGIS.Geometry.IGeometry
    Get
        Geometry = m_Geometry
    End Get
End Property
End Class

Friend Class clsFeatureCollection
Implements System.Collections.IEnumerable

Private m_oCol As Collection
Private m_oColReverse As Collection

Public Sub New()
    MyBase.New()
    m_oCol = New Collection
    m_oColReverse = New Collection
End Sub

Public Sub Add(ByRef pFeature As ESRI.ArcGIS.Geodatabase.IFeature, Optional ByRef strBefore As String = "", Optional ByRef strAfter As String = "", Optional ByRef bReverse As Boolean = False)
    'Create a new cFoo object based on parameters
    'passed to this method, then add the new cFoo to 
    'the private collection, and key it by a
    'unique identifier built into cFoo
    'so we can retrieve it quickly later

    'Add the new foo object to the collection

    If bReverse Then
        m_oColReverse.Add(pFeature.OID.ToString().Trim(), pFeature.OID.ToString().Trim())
    End If

    If Not ContainsItem(pFeature.OID.ToString().Trim()) Then
        If strBefore <> "" Then
            m_oCol.Add(New clsFeature(pFeature.OID, pFeature.ShapeCopy), pFeature.OID.ToString().Trim(), strBefore)
        ElseIf strAfter <> "" Then
            m_oCol.Add(New clsFeature(pFeature.OID, pFeature.ShapeCopy), pFeature.OID.ToString().Trim())
        Else
            m_oCol.Add(New clsFeature(pFeature.OID, pFeature.ShapeCopy), pFeature.OID.ToString().Trim())
        End If
    End If

End Sub

Public Sub AddBefore(ByRef pFeature As ESRI.ArcGIS.Geodatabase.IFeature, ByRef strBefore As String, Optional ByRef bReverse As Boolean = False)
    'Create a new cFoo object based on parameters
    'passed to this method, then add the new cFoo to
    'the private collection, and key it by a
    'unique identifier built into cFoo
    'so we can retrieve it quickly later

    'Add the new foo object to the collection
    If bReverse Then
        m_oColReverse.Add(pFeature.OID.ToString().Trim(), pFeature.OID.ToString().Trim())
    End If

    If Not ContainsItem(pFeature.OID.ToString().Trim()) Then
        If strBefore <> "" Then
            m_oCol.Add(New clsFeature(pFeature.OID, pFeature.ShapeCopy), pFeature.OID.ToString().Trim(), strBefore)
        Else
            m_oCol.Add(New clsFeature(pFeature.OID, pFeature.ShapeCopy), pFeature.OID.ToString().Trim())
        End If
    End If

End Sub

Public Sub AddAfter(ByRef pFeature As ESRI.ArcGIS.Geodatabase.IFeature, ByRef strAfter As String, Optional ByRef bReverse As Boolean = False)
    'Create a new cFoo object based on parameters
    'passed to this method, then add the new cFoo to
    'the private collection, and key it by a
    'unique identifier built into cFoo
    'so we can retrieve it quickly later

    'Add the new foo object to the collection
    If bReverse Then
        m_oColReverse.Add(pFeature.OID.ToString().Trim(), pFeature.OID.ToString().Trim())
    End If

    If Not ContainsItem(pFeature.OID.ToString().Trim()) Then
        If strAfter <> "" Then
            m_oCol.Add(New clsFeature(pFeature.OID, pFeature.ShapeCopy), pFeature.OID.ToString().Trim(), , strAfter)
        Else
            m_oCol.Add(New clsFeature(pFeature.OID, pFeature.ShapeCopy), pFeature.OID.ToString().Trim())
        End If
    End If

End Sub

Public ReadOnly Property Count() As Short
    Get
        'Return the number of objects in m_oCol
        Count = m_oCol.Count()
    End Get
End Property

Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
    GetEnumerator = m_oCol.GetEnumerator
End Function

Public Sub Remove(ByRef vIndex As Object)
    'Remove the specified object. Note here
    'that this method will operate on either
    'the index of the object we want removed
    'or the key of the object we want removed
    m_oCol.Remove(vIndex)
End Sub

Public Function Item(ByRef vIndex As Object) As clsFeature
    'Retrieve the specified object. Note here
    'that this method will operate on either
    'the index of the object we want
    'or the key of the object we want
    Item = m_oCol.Item(vIndex)
End Function

Public Sub Clear()
    'remove all objects from the private collection
    m_oCol = New Collection
    m_oColReverse = New Collection
End Sub

Public Function Reverse(ByRef val_Renamed As Object) As Boolean
    Try
        If m_oColReverse.Contains(val_Renamed) Then
            Return True
        Else
            Return False
        End If
    Catch ex As Exception
        If TypeOf ex Is ArgumentException Or TypeOf ex Is IndexOutOfRangeException Then
            Reverse = False
        End If
    End Try
End Function

Public Function ContainsItem(ByRef val_Renamed As Object) As Boolean
    Try
        If m_oCol.Contains(val_Renamed) Then
            Return True
        Else
            Return False
        End If

    Catch ex As Exception
        If TypeOf ex Is ArgumentException Or TypeOf ex Is IndexOutOfRangeException Then
            ContainsItem = False
        End If
    End Try
End Function

Private Sub Class_Terminate_Renamed()
    'Set up the collection
    m_oCol = Nothing
    m_oColReverse = Nothing
End Sub

Protected Overrides Sub Finalize()
    Class_Terminate_Renamed()
    MyBase.Finalize()
End Sub
End Class
Deke
  • 117
  • 1
  • 1
  • 8
  • @varocarbas so i should be able to use the clsFeature in a particular order? I thought the only way to keep an order was a list or linked list – Deke Aug 27 '15 at 15:47
  • Note that KeyValuePair is a valid .NET structure. It refers, for example, to the elements contained by a dictionary. In C# (in VB.NET, the syntax is similar), if you use dictionary dict in a foreach loop, each iteration would be done on a KeyValuePair variable. (I deleted the original one and rewrote it because wanting to edit one bit and not being allowed to do so. The just-5-mins-for-edition are not enough for a heavy-editor like me :)) – varocarbas Aug 27 '15 at 15:54
  • Could you please write a relevant part of the original code to understand the exact problem better (haven't used vb6 for a while). In any case, you can order any collection as you wish (and even easily reordering them). The rules on this front are not too different for a list/array (of KeyValuePairs) or for a dictionary. – varocarbas Aug 27 '15 at 15:54
  • @varocarbas Thanks I will make an attempt at trying the keyvaluepair in c# – Deke Aug 27 '15 at 15:56
  • Somewhere deep in the recesses of memory I think there as a problem with parameter order when moving between the various types of collections/dictionaries. If you get stuck check the value/key parameter order. – rheitzman Aug 27 '15 at 16:03
  • Not that I'd recommend perpetuating any undesirable designs, but it is worth mentioning that the old-school VB `Collection` class can be used in C# as well. You could just reference the `Microsoft.VisualBasic.dll` library from your c# project and then import the `Microsoft.VisualBasic` namespace. – Steven Doggart Aug 27 '15 at 16:03
  • @varocarbas I was also thinking that but I ran into a casting error. So I would have to change all the m_oCols to a list? – Deke Aug 27 '15 at 16:04
  • @StevenDoggart hey! It has been a long time! Thanks for sharing something which nobody should ever use LOL (-> for the old times) – varocarbas Aug 27 '15 at 16:05
  • @rheitzman I ran into that problem.. keeping the order correct. I tried the various dictionary types and I always ran into a problem – Deke Aug 27 '15 at 16:08
  • @varocarbas Hey!! It's been a while. :) Yup, usually I'm not one to toss bad ideas into the mix, but if the project was one where using the old-school `Collection` class in VB.NET was already acceptable, then it may very well be the type of project where just getting it to work with as little development time as possible is the top-concern. If so, just carrying it over as is may be the *best* (cough) option. – Steven Doggart Aug 27 '15 at 16:09
  • @StevenDoggart Sure. It was just an excuse to say you hi. – varocarbas Aug 27 '15 at 16:13
  • @varocarbas Maybe I'm looking at this backwards. I have this class converted correctly, maybe its the other classes that I need to convert to this structure - Lists, but I dont need to keep the order. Ive looked at so many options my head is spinning :) – Deke Aug 27 '15 at 16:14
  • The summary is that if you want to convert everything literally you can rely on many approaches (look at Steven comment: you might even continue relying on VB Collections). But if you want to do things properly, perhaps should rethink a bit how things are being done. – varocarbas Aug 27 '15 at 16:25
  • @varocarbas It needs to be done properly. I will need the Add etc and the items because other functions access the class from the main class. its just that when I bring tall the m_ocol into one collection I get a cast error and I cant seem to track it. Which makes me question to use lists or dictionaries etc – Deke Aug 27 '15 at 16:29
  • m_oCol(string, clsFeatureObjectCollection) is clearly a KeyValuePair of type and, as suggested in the answer, when you have many KeyValuePairs, you can rely on a Dictionary or on a list or... (or even in a VB collection as suggested). Although all this sounds too inefficient and might be improved. – varocarbas Aug 27 '15 at 16:33
  • One last clarification though: you have many options but all of them are different (and thus have to be treated differently). You might rely on other approaches like interfaces (iList) which allow higher flexibility, but the old VB idea of one thing (collection) rules them all is not applicable anymore. – varocarbas Aug 27 '15 at 16:38
  • @varocarbas Thanks man.. I know its definately confusing.. :) – Deke Aug 27 '15 at 16:38
  • After finally understanding what you want (I think), it seems that your misunderstanding might have been fixed really quickly. Next time you should share relevant code (showing the problems you have clearly; together with your not so clear ideas); not lots of code which is not relevant.... The answer to your problem is: there are many different collections in .NET, all of them have things in common, but are different (and have to be treated different; cannot be directly casted/converted). Think carefully about your requirements to understand (or to let us know about) the best solution for you. – varocarbas Aug 27 '15 at 16:43
  • The clearer is the question (, the error, the misunderstanding, the specific lack of knowledge), the quicker and easier a proper solution would appear :) – varocarbas Aug 27 '15 at 16:44
  • @varocarbas I think the biggest problem is getting it sorted in my head :) I'll be more direct next time. Thanks man Greatly appreciated – Deke Aug 27 '15 at 16:58

1 Answers1

1

You have a couple of different options.

  1. List<KeyValuePair<string, clsFeatureObjectCollection>>
    Use this if you need duplicate keys, fast lookup is not important O(n) and insert order need to be preserved.
  2. Dictionary<string, clsFeatureObjectCollection>
    Use this if keys are unique, you need fast lookup O(1) and order is irrelevant.
  3. SortedDictionary<string, clsFeatureObjectCollection>
    Use this if keys are unique, you need fast lookup O(log n) and the order of the items should follow the key. (Or any comparer you supply)
  4. SortedList<string, clsFeatureObjectCollection>
    Similar to SortedDictionary. See What's the difference between SortedList and SortedDictionary? for comparison.
Community
  • 1
  • 1
Magnus
  • 45,362
  • 8
  • 80
  • 118