0

I have a class called ReDictionary

Edit: My class declaration

Public Class ReDictionary(Of TKey, TValue) Inherits Dictionary(Of TKey, TValue)

which provides minor enhancements for my purposes of a dictionary with things like this.

' Wraps TryGetValue
Public Function ValueOr(key, OnNoValue)
    dim TossBool = Me.TryGetValue(key, TossObj)
    If TossBool Then
        Return TossObj
    Else
        Return OnNoValue
    End If
End Function

I'm trying to write a .Clone() method (shallow) as part of the class.

I've tried a function that uses a TopLevelDictionary.clone(src,dest) syntax and a sub that uses an TopLevelDict("Entry").Clone(src) syntax

Public Sub Clone(srcKey As TKey, destKey As TValue) As ReDictionary(Of object, object)
    Dim newbook As New ReDictionary(Of Object, Object)
    For Each kvp As KeyValuePair(Of Object, Object) In Me(srcKey)
        newbook(kvp.Key) = kvp.Value.ToString()
    Next
    Me(destKey) = newbook
End Sub

This is almost a quasi-code. I've tried probably every combination of TKey/TValue, Object/Object, even down the the specific of String/String which the two dictionaries I want to clone happen to be. I've also tried not specifying argument/return type

The errors I typically get are Cannot Convert ReDictionary(...) to ReDictionary(...) such as when the when the functions temporary ReDictionary is (Object, Object) and the source ReDictionary is (String, String).

I'd like to avoid making my public dictionary (Object, Object) because it seems wasteful on memory.

My two dictionaries are populated within the same function on form load. One is a public and the other is local to the function but passes values (a description selected by language) global dictionary.

I've realized I could probably easily write a clone function (not part of ReDictionary that used a syntax like Dict(NewEntry) = CloneEntry(Dict("Entry"). I had something like this before when I was just trying to clone object in the Public Dictionary.

Private Function CloneObj(key) As ReDictionary(Of String, String)
    Dim inObj = New ReDictionary(Of String, String)
    For Each kvp As KeyValuePair(Of String, String) In pLib(key)
        inObj(kvp.Key) = pLib(key)(kvp.Key)
    Next
    Return inObj
End Function

Although I might possibly run conflicts when I try to relax the object type.

  • The Global dictionary is a Dictionary(String) of Dictionaries(String) of Strings,
  • The local dictionary is a Dictionary(String) of cTranslations(class) with String properties.

At that point, two functions could do it, but I'm just more determined, if it's possible.


Update: This is the version I'm currently trying to use

Public Class ReDictionary(Of TKey, TValue)
    Inherits Dictionary(Of TKey, TValue)
    Public Function Clone(srcKey As Object) As ReDictionary(Of TKey, TValue)
        Dim inObj As New ReDictionary(Of TKey, TValue)
        For Each kvp As KeyValuePair(Of TKey, TValue) In Me
            inObj(kvp.Key) = kvp.Value
        Next
        Return inObj
    End Function
End Class

This is an example usage of that syntax.

Dim Films As New ReDictionary(Of String, ReDictionary(Of String, String)
Films("Edge of Tomorrow") = New ReDictionary(Of String, String)
Films("Edge of Tomorrow")("Description") = "Tom Crues goes to war on Groundhog's Day"
Films("Live, Die, Repeat") = Films.Clone("Edge of Tomorrow")

Films("Birdman") = New ReDictionary(Of String, String)
Films("Birdman")("Description") = "Michael Keyton, who played a superhero 20 years ago, plays an actor who also did"
Films("The Unexpected Virtue of Ignorance") = Films.Clone("Birdman")

I actually change some properties of some of the cloned items, which is why I need to clone rather than just one entry being a reference to a sibling entry.

The function itself is actually not causing any errors, but when I try to use it I get (copied straight from debugger, only indented so that it's easy to read):

The error is pretty obvious

Value of type 'ReDictionary(Of String, ReDictionary(Of String, String))' cannot be converted to 'ReDictionary(Of String, String)

If I change In Me to In Me(srckey), I get

Expression of type TValue which is not a collection type.

Changing the typing of srcKey to TValue or TKey also results in errors, as does removing the typing of srckey.

Regular Jo
  • 5,190
  • 3
  • 25
  • 47
  • So the clone method is cloning a `ReDictionary` that is inside of another `ReDictionary`? Is that correct? – MrZander Mar 27 '17 at 16:55
  • 1
    I don't understand your question well enough to give an answer, but I'd have thought a shallow Clone method would have a signature something like `Clone() As ReDictionary(Of TKey, TValue)`, assuming you have a class `ReDictionary(Of TKey, TValue)`. Also a method like your `ValueOr` function could be written as an extension method on the standard generic Dictionary (or, better, IDictionary), which might obviate the need for a custom dictionary class. – Joe Mar 27 '17 at 16:55
  • I'm having a hard type understanding what you're trying to accomplish. If you could post a code snippet of how you are trying to use this, it might be helpful. Are you only trying to implement a dictionary `Clone` and having typing issues or is the `Clone` performing some special function? What is the type of the top level dictionary? `ReDictionary(Of String, ReDictionary(Of String, String))`? Here is another post that shows how to clone a normal dictionary (trivial) http://stackoverflow.com/questions/2279619/how-do-you-clone-a-dictionary-in-net – MrZander Mar 27 '17 at 17:46
  • @Joe I updated my question with a contrived example. I definitely get what you're saying about `ValueOr` and will look into that, but I'd also like to understand **why** this won't work. Base `Dictionary`'s methods work independent of type and I'd like to understand how. **Thanks for the time.** – Regular Jo Mar 27 '17 at 18:29
  • @Joe Well, you were right, an extension method seemed the best approach. I posted it as an answer myself in case anyone should find it useful. – Regular Jo Mar 28 '17 at 01:52

2 Answers2

1

First things first, I want to point out that you should probably make objects instead of using dictionaries, but I digress.

So, why isn't it working.

First, lets look at the Clone function

Public Class ReDictionary(Of TKey, TValue)
    Inherits Dictionary(Of TKey, TValue)
    Public Function Clone(srcKey As Object) As ReDictionary(Of TKey, TValue)
        Dim inObj As New ReDictionary(Of TKey, TValue)
        For Each kvp As KeyValuePair(Of TKey, TValue) In Me
            inObj(kvp.Key) = kvp.Value
        Next
        Return inObj
    End Function
End Class

Notice how srcKey is never used. So this function is returning a clone of the ReDictionary you are calling it on, not the ReDictionary contained in TopLevel(srcKey)

Next, lets look at how you are using it.

Dim Films As New ReDictionary(Of String, ReDictionary(Of String, String)
Films("Edge of Tomorrow") = New ReDictionary(Of String, String)
Films("Edge of Tomorrow")("Description") = "Tom Crues goes to war on Groundhog's Day"
Films("Live, Die, Repeat") = Films.Clone("Edge of Tomorrow")

As stated above, Films.Clone will return a ReDictionary of the same type of the calling ReDictionary, Meaning you are trying to assign a ReDictionary(Of String, ReDictionary(Of String, String) to Films("Live, Die, Repeat"), which is of type ReDictionary(Of String, String)

These two are obviously different types, therefore you can't do that assignment.

So what needs to be fixed:

The Clone Method

I would recommend you remove the srcKey parameter and use this method to clone the object you are calling it on (which is what it is already doing)

The Usage

Now, because Clone will clone the dictionary you are calling it on, you can do something like this:

Dim Films As New ReDictionary(Of String, ReDictionary(Of String, String)
Films("Edge of Tomorrow") = New ReDictionary(Of String, String)
Films("Edge of Tomorrow")("Description") = "Tom Crues goes to war on Groundhog's Day"
Films("Live, Die, Repeat") = Films("Edge of Tomorrow").Clone()
MrZander
  • 3,031
  • 1
  • 26
  • 50
  • **I really appreciate the feedback**, `srckey` wasn't used in that incarnation (an error made out of exasperation because everything I tried was getting a syntax error when I tried things like `in Me(srcKey)` which was the original purpose which resulted in type conflicts. – Regular Jo Mar 27 '17 at 19:32
0

As Joe said in the comments, an Extension Method ended up working the best but don't see how universal typing is possible short of try/catch or if/else.

I found Dim Args as type() = SourceDict.GetType().GetGenericArguments() but as I can't use variables for object types, such as Dim inObject As New Dictionary(Of Args(0), Args(1)), it seems that repetition is necessary.

Leaving this here in case it helps anyone

Public Module DictionaryExtensions '(Of Tkey, TValue)
    <Extension()>
    Public Function CloneDict(ByVal SourceDict As Object,
                       ByVal SourceKey As String) As IDictionary
        Try
            Dim inObject As New Dictionary(Of String, String)
            For Each kvp As KeyValuePair(Of String, String) In SourceDict(SourceKey)
                inObject(kvp.Key) = SourceDict(SourceKey)(kvp.Key)
            Next
            Return inObject
        Catch
            Dim inObject As New Dictionary(Of String, Translation)
            For Each kvp As KeyValuePair(Of String, Translation) In SourceDict(SourceKey)
                inObject(kvp.Key) = SourceDict(SourceKey)(kvp.Key)
            Next
            Return inObject
        End Try
    End Function

    <Extension()>
    Public Function ValueOr(ByVal SourceObject As Object,
                       ByVal Key As Object,
                       Optional ByVal onnovalue As Boolean = False)
        Dim TossBool as Boolean = SourceObject.TryGetValue(Key, TossObj)
        If TossBool Then
            Return TossObj
        Else
            Return onnovalue
        End If
    End Function

    <Extension()>
    Public Function ValueOr(ByVal SourceObject As Object,
                       ByVal Key As Object,
                       ByVal onnovalue As Object)
        Dim TossBool as Boolean = SourceObject.TryGetValue(Key, TossObj)
        If TossBool Then
            Return TossObj
        Else
            Return onnovalue
        End If
    End Function
End Module
Regular Jo
  • 5,190
  • 3
  • 25
  • 47