Net v4.6 - VS2015
Take the following code:
I have a class (KeywordClass) with some properties, a module with a declaration of
Friend _ImportKeywords As New List(Of String)
and a Write only property on the class of:
Public WriteOnly Property ImportKeywords() As List(Of String)
Set(ByVal value As List(Of String))
_ImportKeywords = value
End Set
End Property
I am trying to pass a list to the property from outside of the class.
Dim Kwords As New List(Of String)
Kwords.add("testing")
Kwords.add("hello")
Kwords.add("goodbye")
Using MyClass As New KeywordClass
MyClass.ImportKeywords(Kwords)
End Using
but Im getting the error:
Too many arguments to 'Public WriteOnly Property ImportKeywords As List(Of String)'
I've done a fair bit of searching and all the results that have been solutions seem to point to just passing the list - which is what I have done above. Nothing else seems to work and Im probably missing the obvious. IIf someone can help me, I'd be very appreciative. Thanks for your time :)
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.
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()
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
If I create an object like this...
Public Class SomeClass
Public Property SomeList As New List(Of Int32)
End Class
...I can alter the list using the normal methods:
Dim s As New SomeClass()
s.SomeList.Add(123)
But, is it possible to allow the above access to the list, but prevent the whole list being replaced by another list instance? For example, prevent this:
Dim s As New SomeClass()
Dim lst As New List(Of Int32)
lst.Add(1)
s.SomeList = lst ' <-- prevent a replacement list being passed
I notice that when using the Net.MailMessage class, there is a Property called To where this seems to have been applied. I can add an email address to the list...
Dim mm as New MailMessage
mm.To.Add(New MailAddress("me#company.com"))
...but I cannot replace the MailAddressCollection:
Dim mm As MailMessage
Dim mc As MailAddressCollection
mm.To = mc ' Error: Property 'To' is 'ReadOnly'
How is this achieved please? I tried to decompile the source of MailMessage but there is so much code I'm struggling to see how it is done.
There are two ways..
Private _SomeList As New List(Of Int32)
Public ReadOnly Property SomeList As IList(Of Int32)
Get
Return _SomeList
End Get
End Property
..as Konrad pointed out in the comments. Having the property return the IList Interface instead of List is a style thing. If you run code analysis, it will suggest returning the IList instead of List.
That will prevent the caller from replacing the list with a whole new list or setting it to Nothing, but there's nothing to stop them from doing something like...
someInstance.SomeList.Clear()
someInstance.SomeList.AddRange(newListOfStuff)
If you really want to restrict what the caller can do with it, you can leave the list private and just implement methods to let the caller do what you want to allow them to do...
Private _SomeList As New List(Of Int32)
Public Sub AddToSomeList(val As Int32)
_SomeList.Add(val)
End Sub
Now the caller can add to the list but not remove or clear the list.
We can do this in vb.net:
Dim d= new Dictionary(of string, string) from {{"a","valA"},{"b","valB"}}
Please how can we make the following possible for convenience:
public sub Setup(args)
Dim d= new Dictionary(of string, string) from args
end sub
Thanks.
No, you can't initialize a dictionary with collection initializer syntax from a variable or parameter. It's just syntactic sugar that the compiler eats if you assign it in the way you've shown in the first snippet.
However, you can pass an IDictionary(Of String, String) and use that for the constructor of the dictionary:
Public sub Setup(dict As IDictionary(Of String, String))
Dim d = new Dictionary(Of String, String)( dict )
End Sub
Or you can pass an IEnumerable(Of KeyValuePair(Of String, String)) and use that for args.ToDictionary(Function(kv) kv):
Public sub Setup(keyVals As IEnumerable(Of KeyValuePair(Of String, String)))
Dim d = args.ToDictionary(Function(kv) kv)
End Sub
The former approach is more efficient, the latter is more general. Allowing IDictionary(Of TKey, TValue) has the advantage that you can pass more types since there are many classes that implement that interface.
I have an extension method that works great in converting an object to my form object.
'Copy an object to a form object
<Extension()> _
Public Function FromModel(ToObject As BaseFormObject, ByRef FromObject As Object) As Boolean
ToObject = FromObject
Return True
End Function
I want to do the same thing for a list of objects.
'Copy a list of objects to form objects
<Extension()> _
Public Function FromModelList(ToList As List(Of BaseFormObject), ByRef FromList As List(Of Object)) As Boolean
For Each FromItem As Object In FromList
'Create a new BaseFormObject for every item in FromList, Add it to our From List
Dim newFormObject = New BaseFormObject()
newFormObject.FromModel(FromItem)
ToList.Add(newFormObject)
Next
Return True
End Function
Creating a new List(Of BaseFormObject) does not give me access to the method "FromModelList." What is the proper way to go about doing this?
Your code works fine.
I created this code:
Dim lst As New List(Of BaseFormObject)
I got the extension member when I typed in lst.
I'm going to go out on a limb here and suggest that you're not actually instantiating a List(Of BaseFormObject), but instead something like List(Of ActualFormObject) where ActualFormObject inherits from BaseFormObject.
So if I use this code:
Dim lst As New List(Of ActualFormObject)
Then you don't get the extension member. If that's what you're hoping for, then it's easy to fix.
Change your code to this:
<Extension()> _
Public Function FromModelList(Of T As {New, BaseFormObject})(ToList As List(Of T), ByRef FromList As List(Of Object)) As Boolean
For Each FromItem As Object In FromList
'Create a new BaseFormObject for every item in FromList, Add it to our From List
Dim newFormObject = New T()
newFormObject.FromModel(FromItem)
ToList.Add(newFormObject)
Next
Return True
End Function
Then you get the extension member on lists of sub-classes.