vb.net class read-only property as list(of T) - vb.net

I am looking for an example of the usage of a read-only property as list(of T)
Public ReadOnly Property oList As List(Of String)
Get
Return ...
End Get
When I normally use list(of T) I always use the New constructor in front of my variable type Public Property oList as New list(of T)
But when I do this now I get an error message from Visual Studio.
So how does this work?
I have never used an read-only property before..

Here's a simple example:
Private myList As New List(Of String)
Public ReadOnly Property List As List(Of String)
Get
Return myList
End Get
End Property
or, using an automatic initialized readonly property (supported in Visual Studio 2015, i.e. VB14 and above):
Public ReadOnly Property List As List(Of String) = New List(Of String)
Now consumers can add and remove from your list:
myObject.List.Add(...)
myObject.List.Remove(...)
but they cannot replace the list as a whole:
myObject.List = someOtherList ' compile error
myObject.List = Nothing ' compile error
This has has a few advantages:
The invariant that List is never Nothing is always ensured.
The consumer of your class cannot do counter-intuitive stuff like "connecting" the lists of two objects:
myObject1.List = myObject2.List ' Both objects reference the same list now
As a side note, I would recommend to expose an interface (IList) in such a situation instead of the concrete class:
Public ReadOnly Property List As IList(Of String) = New List(Of String)
This gives you all of the features mentioned above. In addition, you can later change the concrete type of your list to, for example, MyFancyListWithAdditionalMethods without breaking the contract, i.e., without having to recompile the consumers of your library.

Related

VB.Net Pass a List (of) to a WriteOnly Property sub

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 :)

Allow an object's List(Of T) property to be amended but not replaced

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.

Enforce that .Equals override must compare newly-added properties

I have a class that overrides Equals. This class has many properties, and more properties will be added in the future. How can I enforce that, when new properties are added, the Equals override must be changed to consider those properties?
I have a partial solution, so you can see what I'm trying to do:
Public Class LotsOfProperties
Public Shared ReadOnly properties As New HashSet(Of String) From {"propertyA", "propertyB"}
Public Property propertyA As String
Public Property propertyB As List(Of String)
Public Overloads Function Equals(ByVal otherObj As LotsOfProperties) As Boolean
Dim differences As New List(Of String)
For Each propertyName As String In properties
Dim meValue As Object = getValueByPropertyName(Me, propertyName)
Dim otherObjValue As Object = getValueByPropertyName(otherObj, propertyName)
If Not meValue.Equals(otherObjValue) Then
differences.Add(propertyName)
End If
Next
Return (differences.Count = 0)
End Function
Private Function getValueByPropertyName(ByVal obj As Object, ByVal name As String) As Object
Dim rtnObj As Object
Dim pInfo As Reflection.PropertyInfo = obj.GetType.GetProperty(name)
rtnObj = pInfo.GetValue(obj, Reflection.BindingFlags.GetProperty, Nothing, Nothing, Nothing)
Return rtnObj
End Function
End Class
However, this won't work because I want to use SequenceEqual to compare the List property but not to compare the String property. Because I have to use different methods to test equality of each property, I can't just loop through the properties with reflection.
This seems like a common use-case. Is there a simple solution, or do I need to simply trust that future developers will modify Equals when adding new properties?

Property of type dictionary

Is there a way to declare a property of type dictionary of string, string in VB.Net.
I am using this on a usercontrol to add properties via the designer.
I tried the following:
Private v As Dictionary(Of String, String)
Public Property VList As Dictionary(Of String, String)
Get
Return v
End Get
Set(ByVal value As Dictionary(Of String, String))
v = value
End Set
End Property
But when I try this the string collection editor window opens up but the add & remove buttons are disabled. What is the correct way to declare this property?
I want to add the key & value via the designer.
The Dictionary does not have a built in UITypeEditor. There are many reasons why there isn't: there are 2 Types which are generic, it also doesnt have an Item accessor, there is no simple Add method, the key must be unique and there is no built in way to serialize a Dictionary "item".
The right way is to use a Collection class inheriting from Collection<T> so you can control access to the contents (note: this is from System.Collections.ObjectModel not the horrible VB Collection!). The fast way to setup a working interface is to use a List(Of myTypeClass), but this is dangerous in production code because it allows all sorts of actions on the innerlist which you likely do not want.
<Serializable><TypeConverter(GetType(FooConverter))>
Public Class FooBar
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
Public Property Name As String
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
Public Property Value As String
' simple ctor REQUIRED for the UITypeEditor
Public Sub New()
Name = ""
Value = ""
End Sub
' ctor for the TypeConverter (NOT included)
Public Sub New(n As String, v As String)
Name = n
Value = v
End Sub
Public Overrides Function ToString
Return Name
End Sub
End Class
' must be instanced
Private myFoo As New List(Of FooBar)
' list is an object so it cant be serialized, but the CONTENTS can be
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public Property FooList As List(of FooBar)
Get
If myFoo Is Nothing Then
myFoo = New List(of FooBar)
End If
Return myFoo
End Get
Set
' do nothing
End Set
End Sub
' for designer serialization
Private Function ShouldSerializeFooList As Boolean
Return myFoo.Count > 0 ' or myFoo IsNot Nothing
End Sub
public Sub ResetMyFolist
myFoo = New List(of FooBar)
End Sub
Caveats:
It is almost always better to write a class container for the Foobar items. Usually you would inherit from Collection<T>. List<T> as shown is a container and a collection, so the contents can be cleared, reset, modified etc when exposed as shown. They are fast and easy to implement though and the basic concept is the same.
If a Dictionary is really what you want, you can write your own UITypeEditor (not UIDesigner, this is not a control) but this would probably require a great deal of work on many levels. The reason there are not gobs of them flying around is that most people make do with one of the standard collections and simply enforce unique names in other ways. (Adding "Properties" to a usercontrol, suggests that really the key or name ought to be fixed and known to the app ahead of time so it knows what it is and what to do with it(?)).
Often VS can perform designer serialization on its own with simple properties like those in FooBar. However, since they are items in a collection, you will likely need to also write a TypeConverter which can return an InstanceDescriptor, to help VS instance them. But that is a different question.

How to setup return value for a readonly property using RhinoMocks in VB.NET?

I'm using RhinoMock in VB.NET and I need to set the return value for a readonly list.
Here's what I want to do (but doesn't work):
dim s = Rhino.Mocks.MockRepository.GenerateStub(of IUserDto)()
s.Id = guid.NewGuid
s.Name = "Stubbed name"
s.Posts = new List(of IPost)
It fails on the compile because Posts is a readonly property.
Then I tried a lambda expression, which works fine for Function calls, but not so much for Properties. This fails to compile.
s.Stub(Function(x As IUserDto) x.Posts).Return(New List(Of IPost))
Next (failing) attempt was to use SetupResults, but this failed stating that it cannot be used in Playback mode.
Rhino.Mocks.SetupResult.For(s.Posts).Return(New List(Of IPost))
Which brings me back to my question:
How do I setup a return value for a readonly property using RhinoMocks in VB.NET?
Is IUserDto an interface? If it is then it should just work. If it isn't, then the problem could be that the readonly property in question is not overridable. RhinoMocks can only mock properties/methods which are defined in an interface or can be overridden.
Here is my (clumsy) attempt at proving that the lambda syntax should work:
Imports Rhino.Mocks
Public Class Class1
Public Sub Test()
Dim s = MockRepository.GenerateMock(Of IClass)()
Dim newList As New List(Of Integer)
newList.Add(10)
s.Stub(Function(x As IClass) x.Field).Return(newList)
MsgBox(s.Field(0))
End Sub
End Class
Public Class AnotherClass
Implements IClass
Public ReadOnly Property Field() As List(Of Integer) Implements IClass.Field
Get
Return New List(Of Integer)
End Get
End Property
End Class
Public Interface IClass
ReadOnly Property Field() As List(Of Integer)
End Interface
i.e. I would get a message box with the number 10 displayed on it (I didn't bother to try hooking up a unit-testing framework with this but that shouldn't make a difference) when Class1.Test is invoked.
Hope that helps (it was an interesting exercise trying to work with RhinoMocks in VB.NET in anycase).