I had downloaded a C# project and wanted to work on VB.Net so I decided to convert that from C# to VB.NET and I encountered some problems when it came to implementing interfaces. I keep getting errors in VB.NET about implementation about how I must have Read-Only or Write-Only specifiers. I want to get rid of this error but I don't know how I can achieve this.
I have Three Files:
CustomPaintRichText.vb
IUnderlineableSpellingControl.vb
ISpellingControl.vb
The same goes with C#, however in C# it works fine and I want to try to get it to work exactly like that in VB.net.
CustomPaintRichText.vb:
Public Class CustomPaintRichText
Inherits RichTextBox
Implements IUnderlineableSpellingControl
Public m_underlinedSections As Dictionary(Of Integer, Integer)
Public m_protectedSections As Dictionary(Of Integer, Integer)
Public m_ignoredSections As Dictionary(Of Integer, Integer)
Public Property UnderlinedSections() As Dictionary(Of Integer, Integer)
Get
If m_underlinedSections Is Nothing Then
m_underlinedSections = New Dictionary(Of Integer, Integer)()
End If
Return m_underlinedSections
End Get
Set(value As Dictionary(Of Integer, Integer))
m_underlinedSections = value
End Set
End Property
Public WriteOnly Property ProtectedSections() As Dictionary(Of Integer, Integer)
Set(value As Dictionary(Of Integer, Integer))
m_protectedSections = value
End Set
End Property
Public WriteOnly Property IgnoredSections() As Dictionary(Of Integer, Integer)
Set(value As Dictionary(Of Integer, Integer))
m_ignoredSections = value
End Set
End Property
Private spellingEnabled As Boolean
Private spellingAutoEnabled As Boolean
Private m_isPassWordProtected As Boolean
Private penColour As Pen
Public Property WhatPenColour() As Pen
Get
Return penColour
End Get
Set(value As Pen)
penColour = value
End Set
End Property
Public Property IsSpellingEnabled() As Boolean
Get
Return spellingEnabled
End Get
Set(value As Boolean)
spellingEnabled = value
End Set
End Property
Public Property IsSpellingAutoEnabled() As Boolean
Get
Return spellingAutoEnabled
End Get
Set(value As Boolean)
spellingAutoEnabled = value
If Not spellingEnabled Then
spellingEnabled = value
End If
End Set
End Property
Public Property IsPassWordProtected() As Boolean
Get
Return m_isPassWordProtected
End Get
Set(value As Boolean)
m_isPassWordProtected = value
End Set
End Property
End Class
IUnderlineableSpellingControl.vb:
Public Interface IUnderlineableSpellingControl
Inherits ISpellingControl
Inherits IUnderlineable
End Interface
ISpellingControl.vb:
Public Interface ISpellingControl
<Browsable(True)> _
Property IsSpellingEnabled() As Boolean
Property SelectionStart() As Integer
Property SelectionLength() As Integer
Property SelectedText() As String
Property Text() As String
Property ContextMenuStrip() As ContextMenuStrip
Property WhatPenColour() As Pen
Property Parent() As Control
Event Disposed As EventHandler
Event Enter As EventHandler
Event TextChanged As EventHandler
Property [ReadOnly]() As Boolean
ReadOnly Property IsPassWordProtected() As Boolean
Sub Cut()
Sub Copy()
Sub Paste(clipFormat As DataFormats.Format)
Sub [Select](start As Integer, length As Integer)
Function Focus() As Boolean
Sub Invalidate(invalidateChildren As Boolean)
WriteOnly Property IgnoredSections() As Dictionary(Of Integer, Integer)
End Interface
If I keep the cursor carret next to Implements IUnderlineableSpellingControl and hit ENTER key, within the CustomPaintRichText.vb class, I get:
Public Property ContextMenuStrip1 As ContextMenuStrip Implements ISpellingControl.ContextMenuStrip
Public Sub Copy1() Implements ISpellingControl.Copy
End Sub
Public Sub Cut1() Implements ISpellingControl.Cut
End Sub
Public Event Disposed1(sender As Object, e As EventArgs) Implements ISpellingControl.Disposed
Public Event Enter1(sender As Object, e As EventArgs) Implements ISpellingControl.Enter
Public Function Focus1() As Boolean Implements ISpellingControl.Focus
End Function
Public WriteOnly Property IgnoredSections1 As Dictionary(Of Integer, Integer) Implements ISpellingControl.IgnoredSections
Set(value As Dictionary(Of Integer, Integer))
End Set
End Property
Public Sub Invalidate1(invalidateChildren As Boolean) Implements ISpellingControl.Invalidate
End Sub
Public ReadOnly Property IsPassWordProtected1 As Boolean Implements ISpellingControl.IsPassWordProtected
Get
End Get
End Property
Public Property IsSpellingEnabled1 As Boolean Implements ISpellingControl.IsSpellingEnabled
Public Property Parent1 As Control Implements ISpellingControl.Parent
Public Sub Paste1(clipFormat As DataFormats.Format) Implements ISpellingControl.Paste
End Sub
Public Property ReadOnly1 As Boolean Implements ISpellingControl.ReadOnly
Public Sub Select1(start As Integer, length As Integer) Implements ISpellingControl.Select
End Sub
Public Property SelectedText1 As String Implements ISpellingControl.SelectedText
Public Property SelectionLength1 As Integer Implements ISpellingControl.SelectionLength
Public Property SelectionStart1 As Integer Implements ISpellingControl.SelectionStart
Public Property Text1 As String Implements ISpellingControl.Text
Public Event TextChanged1(sender As Object, e As EventArgs) Implements ISpellingControl.TextChanged
Public Property WhatPenColour1 As Pen Implements ISpellingControl.WhatPenColour
Public Sub CustomPaint1() Implements IUnderlineable.CustomPaint
End Sub
Public Property IsSpellingAutoEnabled1 As Boolean Implements IUnderlineable.IsSpellingAutoEnabled
Public Event KeyDown1(sender As Object, e As KeyEventArgs) Implements IUnderlineable.KeyDown
Public WriteOnly Property ProtectedSections1 As Dictionary(Of Integer, Integer) Implements IUnderlineable.ProtectedSections
Set(value As Dictionary(Of Integer, Integer))
End Set
End Property
Public Sub RemoveWordFromUnderliningList1(wordStart As Integer) Implements IUnderlineable.RemoveWordFromUnderliningList
End Sub
Public Event SelectionChanged1(sender As Object, e As EventArgs) Implements IUnderlineable.SelectionChanged
Public Property UnderlinedSections1 As Dictionary(Of Integer, Integer) Implements IUnderlineable.UnderlinedSections
And when I make changes to the CustomPaintRichText from a form, I will have extra additional controls and ultimately nothing works.
The error is in Implements IUnderlineableSpellingControl. It is underlined saying that: 'CustomPaintRichText' must implement 'Event Disposed(sender As Object, e As System.EventArgs)' for interface 'ISpellingControl'. This is one of 30 errors along with the ..must implement..for interface.
Here's the error list if you want to see what kind of errors I'm getting.
Here are the .cs files in case:
CustomPaintRichText.cs
IUnderlineableSpellingControl.cs
ISpellingControl.cs
Congratulations, you made Hans Passant go "Ugh!" :)
Speaking to his point, though, mixing and matching assemblies compiled from VB, C#, C++/CLI, F#, or whatever, is generally encouraged in the .NET world, and is the practical solution to your problem. However, if you insist on transforming this C# project into its VB equivalent, one needs to understand the differences in how interfaces get implemented between these two languages.
C# has two styles of implementation: implicit and explicit (see http://blogs.msdn.com/b/mhop/archive/2006/12/12/implicit-and-explicit-interface-implementations.aspx). VB has only an explicit style, which doesn't work quite the same as C# (see https://msdn.microsoft.com/en-us/library/28e2e18x.aspx).
All of those "must implement" errors mean pretty much what they say: You must use the Implements keyword on the appropriate members of your subclass, because VB doesn't do implicit implementations of interfaces. That's a C# thing. When you hit the ENTER key with the cursor caret next to Implements IUnderlineableSpellingControl, the IDE generated template code for the affected (apparently missing) members, complete with Implements. It did that in trying to be helpful, but in this case you have to look over the code and put in the Implements clauses where they're needed (and probably get rid off that template code).
C# has a neat implicit style where it will automatically "wire-up" implementations by matching member names between your class and the interfaces being implemented. Should there be more than one interface that have the same member (with the same signature), they will all be implemented with the very same member in your class (see http://blogs.msdn.com/b/mhop/archive/2006/12/12/implicit-and-explicit-interface-implementations.aspx). This can be a wonderful or not-so-good thing depending on the situation.
C# has a limited explicit style. One simply defines a member in the named in the format of InterfaceName.MemberName (see https://msdn.microsoft.com/en-us/library/ms173157.aspx).
VB has only its explicit style, but it allows for a list of interface members in the Implements clause so that they all get implemented by the very same member in your class. This is the work-around for C# implicit implementations that hookup multiple interfaces.
Finally, there are some sources which claim that VB cannot do re-implementations of an interface on a subclass of a superclass that already has it implemented (e.g. http://www.dotnetheaven.com/article/how-to-re-implement-interfaces-in-vb.net). I do not know if that was ever true, but I can aver that the VB of VS 2012 and later allows for such re-implementations.
C# allows you to implement a WriteOnly or ReadOnly property with a read/write property. (VB 2015 also allows this).
You can easily work around this prior to VB 2015 - here's an example for your IsPasswordProtected implementation:
Private ReadOnly Property ISpellingControl_IsPassWordProtected() As Boolean Implements ISpellingControl.IsPassWordProtected
Get
Return IsPassWordProtected
End Get
End Property
Public Property IsPassWordProtected() As Boolean
Get
Return isPassWordProtected_Renamed
End Get
Set(ByVal value As Boolean)
isPassWordProtected_Renamed = value
End Set
End Property
The 'Implements' goes on the new ReadOnly property which calls the existing read/write property.
Related
I am still in the process of re-thinking from VB6 to .Net, so please forgive if this is trivial.
In a class I have properties which can change, and when they do they should raise a Changed event.
Public Class CPT
Public Event Changed()
Private gsText As String
Public Property Text() As String
Get
Return gsText
End Get
Set(ByVal sValue As String)
If sValue <> gsText Then
gsText = sValue
RaiseEvent Changed()
End If
End Set
End Property
End Class
Another class features an Add method, in which it adds new items of above class into a collection.
Public Class UFB
Private goTexts As New Dictionary(Of String, CPT)
Public Sub Add(sKey As String, sText As String)
Dim oPT As New CPT
oPT.Text = sText
goTexts.Add(sKey, oPT)
End Sub
End Class
Obviously, UFB objects can not receive Changed events, because oPT is not declared on module level and thus can not feature WithEvents.
What is the .Net way to enable UFB to listen to CPT's Changed events (which appear in many other CPT properties).
I got a class containing integer,bool,string and list (of integer). Each variable that need to be serialize in that class has a public property. When i deserialize my class via XmlSerializer the public property of each variable is call. Except the variable that are list of. The List of varaibles are weel deserialisze but the property setter isn't called.
Here is the property :
Public Property Ana_Offset As List(Of Integer)
Get
Return _Ana_Offset
End Get
Set(value As List(Of Integer))
Tmp_Val = _Ana_Offset
_Ana_Offset = value
RaiseEvent VariableChanged(_Ana_Offset, Tmp_Val, "_Ana_Offset", 0)
End Set
End Property
The class is something like this
<Serializable()> Public Class SACCVar
Private _Code_Produit As String
Private _Ana_Offset As New List(Of Integer)
Public Event VariableChanged(ByVal Val As Object, ByVal Old_Val As Object, desc As String, index As Integer)
End Class
The strange fact i just realise while posting is that i got no Set "event" but the get event is fired wit no data return, but for other variable the get isn't fired..?
Thanks for help
I have a class that inherits from CollectionBase. I tried to use the contains method to detect whether the Key already exists before inserting a new one. Here is what I have tried.
<Serializable()> Public Class validationList
Inherits CollectionBase
Public Function Add(ByVal Item As validationItem) As Integer
Return Me.List.Add(Item)
End Function
Default Public ReadOnly Property Item(ByVal index As Integer) As validationItem
Get
Return CType(List.Item(index), validationItem)
End Get
End Property
Public Sub Remove(ByVal index As Integer)
Me.List.RemoveAt(index)
End Sub
Protected Overrides Sub OnInsert(ByVal index As Integer, ByVal value As Object)
If Me.List.Contains(value) Then MsgBox("Already exist")
MyBase.OnInsert(index, value)
End Sub
Public Function IndexOf(ByVal key As validationItem)
Return List.IndexOf(key)
End Function
Public Sub AddRange(ByVal item() As validationItem)
For counter As Integer = 0 To item.GetLength(0) - 1
List.Add(item(counter))
Next
End Sub
End Class
<Serializable()> Public Class validationItem
Implements IEquatable(Of validationItem)
Private _key As validationTypes
Private _value As String
Public Sub New()
' Empty constructor is needed for serialization
End Sub
Public Sub New(ByVal k As validationTypes, ByVal v As String)
_key = k
_value = v
End Sub
Public Enum validationTypes
Madatory = 0
[Integer] = 1
Numeric = 2
[Decimal] = 3
MaxValue = 4
MinValue = 5
MinLength = 6
Email = 7
End Enum
Public Property Value As String
Get
Return _value
End Get
Set(ByVal Value As String)
_value = Value
End Set
End Property
Public Property Key As validationTypes
Get
Return _key
End Get
Set(ByVal value As validationTypes)
_key = value
End Set
End Property
Protected Overloads Function Equals(ByVal eqItem As validationItem) As Boolean Implements IEquatable(Of Testing_Project.validationItem).Equals
If eqItem Is Nothing Then Return False
Return Me._key = eqItem.Key
End Function
Public Overrides Function Equals(ByVal eqItem As Object) As Boolean
If eqItem Is Nothing Then Return False
Dim eqItemObj As validationItem = TryCast(eqItem, validationItem)
If eqItemObj Is Nothing Then Return False
Return Equals(eqItemObj)
End Function
Public Overrides Function GetHashCode() As Integer
Return Me._key.GetHashCode()
End Function
End Class
The validationList will be exposed from a usercontrol as a property, so that items could be added from the designer. When adding items I need to detect whether they already exist. I tried overriding the OnInsert but this sometime return that duplicates exists even when their aren't and doesn't report that duplicate exist when I try to add existing keys.
This indirectly answers the question after dealing with the issue which emerged in comments about Collection(Of T):
Add a reference to System.Collections.ObjectModel if needed, then
Imports System.Collections.ObjectModel
' a ValidationItem collection class
Public Class ValidationItems
Inherits Collection(Of ValidationItem)
Public Shadows Sub Add(NewItem As ValidationItem)
' test for existence
' do not add if it is not unique
Dim dupe As Boolean = False
For n As Int32 = 0 To Items.Count - 1
If Items(n).Key = NewItem.Key Then
dupe = True
Exit For
End If
Next
If dupe = False then
items.Add(newitem)
End if
' I would actually use an IndexOfKey function which might
' be useful elsewhere and only add if the return is -1
If IndexOfKey(NewItem.Key) <> -1 Then
Items.Add(newItem)
End If
End Sub
Some NET collection types implement Add as a function and return the item added. This sounds weird since you pass it the item to add. But returning Nothing if the item cannot be added is a neat semaphore for "I cant/wont do that". I cant recall if the std NET Collection Editor recognizes that or not.
One problem with using Contains is that it will test if item passed as param is the same object as one in the list. They never will be the same object, even if they have the same values. Testing the key in a loop is simpler than calling a method which implements an interface. (That previous answer was totally valid in the context presented, but the context has changed).
Even if you stay with CollectionBase, you want to handle it in the Add. If you try to remove it in OnInsert, VS will have problems deserializing the collection.
Also, your validationitem needs a Name property or the Collection Editor will display "Myassembly+MyType" as the Name (or a ToString override).
Other issues:
I am not sure your IndexOf will work. The list contains ValidationItems (objects), but you check it for _key (string). This will not matter if you change to Collection(Of T) which implements it for you.
The simple ctor is needed by the Collection Editor, not serialization. But the important thing is that it is there.
As for the comment about all Zeroes coming back - that is because your ValidationItem is not yet decorated for designer serialization. Maybe not the Collection Property either, that isnt shown.
So I implemented my own form of Enum static classes so I can associate strings to enumeration values. One of the shared functions in each enum class, GetValue, is used to quickly look up the string value from an internal array via the offset. While all of the enum classes implement the same exact function, the data types of their parameters differ, specific to the enums in each class.
So I come to a point where I want to create a generic class that can use any of the enums by passing it as a generic type parameter. But I cannot find a way to constrain type T in such a way to be able to call each enum's GetValue function. This is where an interface would come in handy, but VB.NET forbids interfaces for shared methods.
I have a base class that I could constrain to, but the base class doesn't implement the GetValue method, and I can't define a generic version of it, because I need to rely on shared properties of each child class in order for GetValue to do its thing.
Is there another way to tackle this?
EDIT:
Here's what an example enum class looks like. The parent, EBase is nothing special, just hosting common Name, Value, and Type properties, plus two shared operators, op_Equality and op_Inequality. Imagine using a couple of these enum classes with a generic method (or class) and needing to access the GetValue member via the generic type parameter T.
Friend NotInheritable Class EExample
Inherits EBase
Private Sub New()
End Sub
Friend Shared Function GetValue(ByVal Name As String) As Enums
Return Enums(Array.IndexOf(_Names, Name))
End Function
'Num of Enums defined.
Friend Shared ReadOnly MaxEnums As Int32 = 5
'String literals.
Private Shared ReadOnly _Names As String() = New String() _
{"one_adam", "two_boy", "three_charles", "four_david", "five_edward"}
'Enums.
Friend Shared ReadOnly OneA As New Enums(_Names(0), 0)
Friend Shared ReadOnly TwoB As New Enums(_Names(1), 1)
Friend Shared ReadOnly ThreeC As New Enums(_Names(2), 2)
Friend Shared ReadOnly FourD As New Enums(_Names(3), 3)
Friend Shared ReadOnly FiveE As New Enums(_Names(4), 4)
'Enum Values Array.
Friend Shared ReadOnly Values As Enums() = New Enums() _
{OneA, TwoB, ThreeC, FourD, FiveE}
Friend NotInheritable Class Enums
Inherits EBase
Private Sub New()
End Sub
Friend Sub New(ByVal Name As String, ByVal Value As Int32)
MyBase.Name = Name
MyBase.Value = Value
MyBase.Type = GetType(Enums)
End Sub
End Class
End Class
EDIT2:
Here's how the things are used:
Dim Foo As EExample.Enums
Foo = EExample.TwoB
Debug.Print(Foo.Name)
will print two_boy
How about this as a solution:
Friend NotInheritable Class EExample
Inherits EBase
Protected Sub New()
End Sub
Protected Overrides Function BuildValues() As EBase.Enums()
Return New Enums() _
{ _
Build(Of EExample)("one_adam", 0), _
Build(Of EExample)("two_boy", 1), _
Build(Of EExample)("three_charles", 2), _
Build(Of EExample)("four_david", 3), _
Build(Of EExample)("five_edward", 4) _
}
End Function
Private Shared _instance As EExample = New EExample()
Friend Shared ReadOnly OneA As Enums = _instance.Values(0)
Friend Shared ReadOnly TwoB As Enums = _instance.Values(1)
Friend Shared ReadOnly ThreeC As Enums = _instance.Values(2)
Friend Shared ReadOnly FourD As Enums = _instance.Values(3)
Friend Shared ReadOnly FiveE As Enums = _instance.Values(4)
End Class
EBase then looks like this:
Public MustInherit Class EBase
Protected MustOverride Function BuildValues() As Enums()
Private _values As EBase.Enums()
Friend ReadOnly Property Values() As EBase.Enums()
Get
If _values Is Nothing Then
_values = Me.BuildValues()
End If
Return _values
End Get
End Property
Friend ReadOnly MaxAlterEnums As Int32 = Me.Values.Length
Friend Function GetValue(ByVal Name As String) As Enums
Return Me.Values.Where(Function(n) n.Name = Name).FirstOrDefault()
End Function
Friend Shared Function Build(Of T As EBase)(ByVal name As String, ByVal value As Int32) As Enums
Return New Enums(name, value, GetType(T))
End Function
Public Class Enums
Public Sub New(ByVal name As String, ByVal value As Int32, ByVal type As Type)
Me.Name = name
Me.Value = value
Me.Type = type
End Sub
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal Value As String)
_name = Value
End Set
End Property
Private _value As Integer
Public Property Value() As Integer
Get
Return _value
End Get
Set(ByVal Value As Integer)
_value = Value
End Set
End Property
Private _type As Type
Public Property Type() As Type
Get
Return _type
End Get
Set(ByVal Value As Type)
_type = Value
End Set
End Property
End Class
End Class
Now your sample code works exactly as before:
Dim Foo As EExample.Enums
Foo = EExample.TwoB
Debug.Print(Foo.Name)
And you are dealing with plain instances for you code implementation. The only Shared values are the five Enums and a private _instance variable.
You can now restrict on EBase.
Does this work for you?
Kumba pointed out that I didn't expose the GetValue function on the EExample class.
Just add this to EExample:
Friend Shared Function GetValue(ByVal Name As String) As Enums
Return _instance.GetValue(Name)
End Function
Wow, this sure sounds like it's getting deep quick. Are you sure you need all that?
Wouldn't it be easier to associate the strings to enumeration values by creating a class, having the enums in the same file (or even nested in the same namespace, if you prefer), and then using a .ToFriendlyName() method to extract the string (that is, if it's designed for a human), and if you need to go the other way, you could create a constructor of the class that has a string argument and stores the associated enum.
Do you really need all of the arrays, inheritance and .GetValue overloads? Maybe I'm misunderstanding, but from your initial description it sounds like the solution might be over-engineered by quite a bit.
Can you provide a bit more detail, especially regarding the "enum static classes" that you started from? What's the root problem you're trying to solve?
In VB6, there used to be a Collection data type that would allow retrieval of an item in the collection by either its key or its ordinal. However, it wasn't strongly typed.
Now, with VB.Net, I am looking for a suitable replacement that is strongly type and could be used with a generic collection.
This is a simple example of what I want to do. The only problem is that the underlying collection class, BindingList, does not support efficient retrieval of an item by an alpha key, so I have to loop through the elements to get the item I am looking for. For large collections, this is not efficient.
I have looked though the various Collection type classes and have found no suitable replacement.
This is what I want to do, except for the looping that is done with the Item property.
Rather than just saying "Use Hash tables" or something like that, if you could, please include the detailed out as I have done for the short example below.
Public Class Car
Public Sub New(ByVal keyName As String, ByVal property1 As String)
_KeyName = keyName
_Property1 = property1
End Sub
Dim _KeyName As String
Public Property KeyName() As String
Get
Return _KeyName
End Get
Set(ByVal value As String)
_KeyName = value
End Set
End Property
Public _Property1 As String
Public Property Property1() As String
Get
Return _Property1
End Get
Set(ByVal value As String)
_Property1 = value
End Set
End Property
End Class
Public Class Cars
Inherits System.ComponentModel.BindingList(Of Car)
Public Overloads ReadOnly Property Item(ByVal key As String) As Car
Get
For Each CurrentCar As Car In Me.Items
If CurrentCar.KeyName = key Then
Return CurrentCar
End If
Next
Return Nothing
End Get
End Property
End Class
I believe you're looking for Dictionary<TKey, TValue>. In fact, if you do want your own collection class that's strongly typed and isn't (itself) generic, if you change your parent class to Dictionary<string, Car>, you should be all set. This all does, of course, assume that you add the cars to the collection with an explicit string key. If you want the lookup to be based on the value of a property in the collection, you'd do better either using or inheriting from List<Car> and using LINQ to query the list. You could then have...
Public Overloads ReadOnly Property Item(ByVal key As String) As Car
Get
Return (from c in Me where c.KeyName = key select c).SingleOrDefault()
End Get
End Property
Do you really need both access by key AND index?
If you do not, then use a Dictionary(Of String, Car), and use
- MyCol.Items("XXX") to retrieve an item by key (or the shorthand MyCol("XXX"))
- MyCol.ContainsKey("XXX") to test if a key exists in the collection
- For Each Entry as KeyValuePair(Of String, Car) in MyCol if you want to enumerate all objects AND their key
- For Each Entry as Car in MyCol.Values if you want to enumerate the entries without consideration for the key
If you need both access by index and key, I'm afraid your best bet is to use a List(of Car) and a Dictionary(of Car) rolled into one custom collection class, because I believe they went away from that kind of collection which is not really all that useful for most problems.
This is what I am thinking is my best solution. I welcome comments for a better way!
Imports Microsoft.VisualBasic
Public Class Car
Implements Xs(Of Car).IKeyName
Private _KeyName As String
Public Sub New(ByVal keyName As String, ByVal property1 As String)
_KeyName = keyName
_Property1 = property1
End Sub
Public Property KeyName() As String Implements Xs(Of Car).IKeyName.KeyName
Get
Return _KeyName
End Get
Set(ByVal value As String)
_KeyName = value
End Set
End Property
Public _Property1 As String
Public Property Property1() As String
Get
Return _Property1
End Get
Set(ByVal value As String)
_Property1 = value
End Set
End Property
End Class
Public Class Cars
Inherits System.ComponentModel.BindingList(Of Car)
Public Overloads ReadOnly Property Item(ByVal key As String) As Car
Get
For Each CurrentCar As Car In Me.Items
If CurrentCar.KeyName = key Then
Return CurrentCar
End If
Next
Return Nothing
End Get
End Property
End Class
Public Class X
Private _KeyName As String
Public Property KeyName() As String
Get
Return _Keyname
End Get
Set(ByVal value As String)
_Keyname = value
End Set
End Property
End Class
Public Class Xs(Of X)
Inherits Hashtable
Interface IKeyName
Property KeyName() As String
End Interface
Public Shadows Sub Add(ByVal item As IKeyName)
MyBase.Add(item.KeyName, item)
End Sub
Public Shadows ReadOnly Property Item(ByVal key As String) As x
Get
If Me.ContainsKey(key) Then
Return MyBase.Item(key)
Else
'If I mispell a key, I don't want to end up creating a new mispelled version, I want an error
Throw New Exception("Element with key " & key & " is not found")
End If
End Get
End Property
End Class
Public Class Cars2
Inherits Xs(Of Car)
End Class
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim MyCar As New Car("key1", "prop1")
'First approach
Dim MyCars As New Cars
MyCars.Add(MyCar)
Dim RetrievedCar As Car = MyCars.Item("key1") 'Inefficient retrieval by key (uses looping)
'Second approach
Dim Cars2 As New Cars2
Cars2.Add(MyCar)
Dim RetrievedCar2 As Car = Cars2.Item("key1") 'Can now efficiently retreive an item by its key
End Sub
The OrderedDictionary in the System.Collections.Specialized namespace can be accessed by index and by key, if you ever need that. But looking at your solution, it looks like a standard Dictionary, but less efficient because it forces a string type for keys.
Is there any reason you can't use the Dictionary .NET provides you, or another collection type that's already in .NET like OrderedDictionary?