1 Attachment(s)
Inheriting a generic Class based on Generic Interface
I want to define a generic interface which will be implemented by an abstract Generic Class. Basically this generic class is a collection class of any class.
Interfaces are in a separate project saved as FileReconciliation.
Here are the interface definitions
Code:
Imports System.Collections
Public Interface ICollectionCommon(Of T As Class)
Inherits IEnumerable
Function Exists(ByVal oKey As Object) As Boolean
ReadOnly Property Count() As Long
Sub Remove(ByVal oKey As Object)
Function Add(Optional ByVal oObject As T = Nothing, Optional ByVal sKey As String = "") As T
Default ReadOnly Property Item(ByVal oKey As Object) As T
End Interface
Public Interface ITag
Property Tag() As String
Property TagValue() As String
End Interface
Public Interface ITransaction
Property Tags() As ICollectionCommon(Of ITag)
End Interface
Public Interface IFileInformation
ReadOnly Property FileName() As String
ReadOnly Property FileType() As FileType
ReadOnly Property BICForwardingBank() As String
ReadOnly Property BICExecutingBank() As String
ReadOnly Property GetFileLevelTags() As ICollectionCommon(Of ITag)
ReadOnly Property TimeStamp() As String
ReadOnly Property Transactions() As ICollectionCommon(Of ITransaction)
Sub GetFileInfo(ByVal sFileName As String)
End Interface
Public Enum FileType
frFileTypeUnknown
frFileTypeDriectDebit
frFileTypeDirectCredit
End Enum
Here is the code for CollectionClass which implements ICollectionCommon
Code:
Imports FileReconciliation
Public MustInherit Class CollectionClass(Of AnyObject As {Class, New})
Implements ICollectionCommon(Of AnyObject)
Private clnCollectionOfOjects As Collection
Public Function Add(Optional ByVal oAnyObject As AnyObject = Nothing, Optional ByVal sKey As String = "") As AnyObject Implements ICollectionCommon(Of AnyObject).Add
Dim objNewMember As AnyObject
If oAnyObject Is Nothing Then
objNewMember = New AnyObject
Else
objNewMember = oAnyObject
End If
If sKey.Trim().Length = 0 Then
clnCollectionOfOjects.Add(objNewMember)
Else
clnCollectionOfOjects.Add(objNewMember, sKey)
End If
Return objNewMember
End Function
Public ReadOnly Property Count() As Long Implements ICollectionCommon(Of AnyObject).Count
Get
Count = clnCollectionOfOjects.Count
End Get
End Property
Public Function Exists(ByVal oKey As Object) As Boolean Implements ICollectionCommon(Of AnyObject).Exists
If clnCollectionOfOjects(oKey) Is Nothing Then
Return False
Else
Return True
End If
End Function
Default Public ReadOnly Property Item(ByVal oKey As Object) As AnyObject Implements ICollectionCommon(Of AnyObject).Item
Get
Item = clnCollectionOfOjects(oKey)
End Get
End Property
Public Sub Remove(ByVal oKey As Object) Implements ICollectionCommon(Of AnyObject).Remove
clnCollectionOfOjects.Remove(oKey)
End Sub
Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
GetEnumerator = clnCollectionOfOjects.GetEnumerator
End Function
Public Sub New()
clnCollectionOfOjects = New Collection
End Sub
Protected Overrides Sub Finalize()
clnCollectionOfOjects = Nothing
End Sub
End Class
Other Classes based on other interfaces are as follows
Code:
Imports FileReconciliation
Public Class clsFileInformation1
Implements IFileInformation
Dim strFileName As String
Dim intFileType As FileType
Dim strBICForwardingBank As String
Dim strBICExecutingBank As String
Dim strTimeStamp As String
Dim objFileLevelTags As clsTags
Dim objTransactions As clsTransactions
Public Sub New()
objFileLevelTags = New clsTags()
objTransactions = New clsTransactions()
End Sub
Protected Overrides Sub Finalize()
objFileLevelTags = Nothing
objTransactions = Nothing
MyBase.Finalize()
End Sub
Public ReadOnly Property BICExecutingBank() As String Implements FileReconciliation.IFileInformation.BICExecutingBank
Get
BICExecutingBank = strBICExecutingBank
End Get
End Property
Public ReadOnly Property BICForwardingBank() As String Implements FileReconciliation.IFileInformation.BICForwardingBank
Get
BICForwardingBank = strBICForwardingBank
End Get
End Property
Public ReadOnly Property FileName() As String Implements FileReconciliation.IFileInformation.FileName
Get
FileName = strFileName
End Get
End Property
Public ReadOnly Property FileType() As FileReconciliation.FileType Implements FileReconciliation.IFileInformation.FileType
Get
FileType = intFileType
End Get
End Property
Public Sub GetFileInfo(ByVal sFileName As String) Implements FileReconciliation.IFileInformation.GetFileInfo
Dim objTransaction As clsTransaction
Dim objTag As clsTag
Dim objTags As clsTags
strFileName = sFileName
objTags = New clsTags()
objTag = objTags.Add()
objTag.Tag = ":20:"
objTag.TagValue = ":20: Value"
objTag = objTags.Add(sKey:=":30:")
objTag.Tag = ":30:"
objTag.TagValue = ":30: Value"
objTransaction = objTransactions.Add()
objTransaction.Tags = objTags 'exception is thrown here
objFileLevelTags = objTags
End Sub
Public ReadOnly Property GetFileLevelTags() As FileReconciliation.ICollectionCommon(Of FileReconciliation.ITag) Implements FileReconciliation.IFileInformation.GetFileLevelTags
Get
GetFileLevelTags = objFileLevelTags
End Get
End Property
Public ReadOnly Property TimeStamp() As String Implements FileReconciliation.IFileInformation.TimeStamp
Get
TimeStamp = strTimeStamp
End Get
End Property
Public ReadOnly Property Transactions() As FileReconciliation.ICollectionCommon(Of FileReconciliation.ITransaction) Implements FileReconciliation.IFileInformation.Transactions
Get
Transactions = objTransactions
End Get
End Property
End Class
Public Class clsFilesInformation
Inherits CollectionClass(Of clsFileInformation1)
Public Sub New()
MyBase.New()
End Sub
End Class
Public Class clsTag
Implements ITag
Private strTagID As String
Private strTagValue As String
Public Sub New()
strTagID = ""
strTagValue = ""
End Sub
Friend Sub New(ByVal sTagID As String)
strTagID = sTagID
strTagValue = ""
End Sub
Friend Sub New(ByVal sTagID As String, ByVal sTagValue As String)
strTagID = sTagID
strTagValue = sTagValue
End Sub
Public Property Tag() As String Implements ITag.Tag
Get
Tag = strTagID
End Get
Friend Set(ByVal value As String)
strTagID = value
End Set
End Property
Public Property TagValue() As String Implements ITag.TagValue
Get
TagValue = strTagValue
End Get
Friend Set(ByVal value As String)
strTagValue = value
End Set
End Property
End Class
Public Class clsTags
Inherits CollectionClass(Of clsTag)
Public Sub New()
MyBase.New()
End Sub
End Class
Public Class clsTransaction
Implements ITransaction
Dim objTags As clsTags
Public Property Tags() As ICollectionCommon(Of ITag) Implements ITransaction.Tags
Get
Tags = objTags
End Get
Set(ByVal value As ICollectionCommon(Of ITag))
objTags = value
End Set
End Property
End Class
Public Class clsTransactions
Inherits CollectionClass(Of clsTransaction)
Public Sub New()
MyBase.New()
End Sub
End Class
Sub Main is follows in a separate module
Imports FileReconciliation
Code:
Module StartUP
<STAThread()> _
Sub Main()
Dim objTags As clsTags
Dim objFilesInformation As clsFilesInformation
Dim objFileInformation As clsFileInformation1
objFilesInformation = New clsFilesInformation
objFileInformation = objFilesInformation.Add()
objFileInformation.GetFileInfo("SomeFile")
objTags = objFileInformation.GetFileLevelTags
objFileInformation = Nothing
objFilesInformation = Nothing
End Sub
End Module
Line of code, in second last code section in Sub GetFileInfo, marked as Bold is where an error occurs.
Can somebody point out what I'm doing wrong?
Attachment 156249
Re: Inheriting a generic Class based on Generic Interface
There is no bold line. It took me some time to find the line you meant. Also, with all that code, it's far from obvious what the issue is so perhaps you could tell us what type of exception is thrown and what the error message is.
Re: Inheriting a generic Class based on Generic Interface
the exception thrown is as follows
Unable to cast object of type 'FileRecon.clsTags' to type 'FileReconciliation.ICollectionCommon`1[FileReconciliation.ITag]'.
I put all the code is seperate pieces so that its easy for anyone to give it run so that they can figure out what I'm doing wrong.
Interface Definition is in a seperate project. Rest of the code is in seperate project. Thx
Re: Inheriting a generic Class based on Generic Interface
Still Waiting for anyone to help me. I'll be grateful if somebody come forward and point out why this error is come up and what can fix it.
The basic idea is to define the CollectionClass only once so we can define as many collection classes of any class just by inheriting CollectionClass
Thx.
Re: Inheriting a generic Class based on Generic Interface
Hi,
After a long time I am back with the same question. I will be very grateful if someone will help me why this code is throwing an error. What is wrong with generic interfaces and their implementation? The project code is attached in OP.
Thnaks
Re: Inheriting a generic Class based on Generic Interface
You STILL haven't told us what line throws that exception.
Re: Inheriting a generic Class based on Generic Interface
But here's my guess:
Code:
Public Property Tags() As ICollectionCommon(Of ITag) Implements ITransaction.Tags
Get
Tags = objTags
End Get
Set(ByVal value As ICollectionCommon(Of ITag))
objTags = value
End Set
End Property
That's in the class "clsTransaction", which I guess is short for "Cool Layered Salsa Transaction". 'objTags' is a clsTags, so let's see if we can assign an ICollectionCommon(Of ITag) to it.
Code:
Public Class clsTags
Inherits CollectionClass(Of clsTag)
Nope. CollectionClass(Of clsTag) is very specific. So is clsTags. If you want to assign something to a variable of type clsTag, it has to either be something that DERIVES from clsTags or is a clsTags itself.
Think of it this way.
"Fruit" is like an interface. If I tell you "eat fruit" there are a lot of different things it could be, like an apple, orange, or banana.
"Apple" is like a class. While there are many apple varieties, if I ask you for an apple you know not to get me a banana.
"Fuji Apple" is a very specific Apple.
So if I have a variable of type "Fuji Apple", I can't accept a Granny Smith Apple, even though they're both Apples. And I certainly can't accept a Banana, because even though they are both Fruits, a Banana is not a "Fuji Apple".
You've built a very complex hierarchy, so it'd take me a while to guess what you wanted so I could help you design what would work. My guess is:
- 'clsTags' should be CollectionClass(Of ITag).
- The property type of 'clsTransaction.Tags' should be CollectionClass(Of ITag).
By making 'clsTags' slightly more abstract, and the property type slightly more concrete, you ought to be able to move on.
As an aside, stop using warts like 'cls' to mark your classes. .NET professionals agreed 14 years ago this practice was not as important in .NET as it was in COM. We understand now if a type is CapitalizedLikeThis and has a NounName, it is a Class and we don't need more help to figure that out. Extra letters add to the cognitive load.
Re: Inheriting a generic Class based on Generic Interface
For what it's worth, the line throwing the exception is line 62 in the block "other classes code" of post #1:
Code:
objTransaction.Tags = objTags 'exception is thrown here
Presumably the code didn't have valid [Code] tags for vBForums so the Bold didn't show up. To think that defeated some of the mightiest intelligences of this forum;) back in 2009! Still, the actual question is way above my head...:).
BB
Re: Inheriting a generic Class based on Generic Interface
Quote:
Originally Posted by
Sitten Spynne
Nope. CollectionClass(Of clsTag) is very specific. So is clsTags. If you want to assign something to a variable of type clsTag, it has to either be something that DERIVES from clsTags or is a clsTags itself.
Think of it this way.
"Fruit" is like an interface. If I tell you "eat fruit" there are a lot of different things it could be, like an apple, orange, or banana.
"Apple" is like a class. While there are many apple varieties, if I ask you for an apple you know not to get me a banana.
"Fuji Apple" is a very specific Apple.
So if I have a variable of type "Fuji Apple", I can't accept a Granny Smith Apple, even though they're both Apples. And I certainly can't accept a Banana, because even though they are both Fruits, a Banana is not a "Fuji Apple".
You've built a very complex hierarchy, so it'd take me a while to guess what you wanted so I could help you design what would work. My guess is:
- 'clsTags' should be CollectionClass(Of ITag).
- The property type of 'clsTransaction.Tags' should be CollectionClass(Of ITag).
By making 'clsTags' slightly more abstract, and the property type slightly more concrete, you ought to be able to move on.
As an aside, stop using warts like 'cls' to mark your classes. .NET professionals agreed 14 years ago this practice was not as important in .NET as it was in COM. We understand now if a type is CapitalizedLikeThis and has a NounName, it is a Class and we don't need more help to figure that out. Extra letters add to the cognitive load.
First of all I am very grateful to you for your response. You advice is absolutely right regarding naming conventions. This code was written in 2009. I had just started coding in VB.NET and COM naming convention was still very fresh back in my mind. So pardon me for that.
This was a conceptual question. I know this situation is very unlikely in real world applications. I was learning generic interfaces so I imagined a situation and implemented it. Again this is a pure conceptual question regarding generic interfaces and their implementations.
I am getting your point. I will try by changing it and let you know.
By the way, did you manage to download the project in zip file in my last post? Please let me know if it is not reachable.
I had added it so anybody can just run it straight away.
Thanks again for your time and a hint for the solution.
Re: Inheriting a generic Class based on Generic Interface
I tried with the above suggestion but still throwing error.
I have attached the project as zip file directly in the opening post. The project ready to compile and run (obviously with the run time error).
Thanks.
Re: Inheriting a generic Class based on Generic Interface
I don't think I'd write ICollectionCommon in my code, the methods it requires are already provided by List(Of T). If you do create an interface, that tends to be the type you want to use for a property. So all of your properties that are of type CollectionClass instead of ICollectionCommon are problematic.
Essentially you've just reimplemented List(Of T). I don't tend to bother with inheritance when I have generic parameters. What I mean is instead of this:
Code:
Public Class AppleList
Inherits List(Of Apple)
End Class
I just declare a variable of type List(Of Apple). There's not a lot of semantic value in declaring a class for it. I think it's best to only derive from a class if you plan on changing how it behaves.
So let's look at the line that throws the exception, walk back to why it throws, then discuss how I might envision the "right" solution.
Code:
objTransaction.Tags = objTags 'exception is thrown here
'objTransaction' is clsTransaction, and its 'Tags' property is expecting 'ICollectionCommon(Of ITag)'. OK. At this point in time, 'objTags' is of type clsTags. That implements ICollectionCommon(Of ITag). So far so good. But let's look inside the property:
Code:
Dim objTags As clsTags
Public Property Tags() As ICollectionCommon(Of ITag) Implements ITransaction.Tags
Get
Tags = objTags
End Get
Set(ByVal value As ICollectionCommon(Of ITag))
objTags = value
End Set
End Property
'value' is ICollectionCommon(Of ITag). You are giving it a clsTags, but right now the compiler doesn't know that. 'objTags' is of type clsTags. So you CANNOT assign ICollectionCommon(Of ITag) to it, because that is what's known as a "downcast" and they are not always safe.
To illustrate, I will go back to my fruit analogy. Let's say we have this:
Code:
Public Interface IFruit
End Interface
Public Class Apple
Implements IFruit
End Class
Public Class Banana
Implements IFruit
End Class
Your code looks like this, and it can't work:
Code:
Dim fruit As IFruit = New Banana()
Dim apple As Apple = fruit
Even though the Banana is masquerading as an IFruit, when the compiler tries to assign it to Apple it has to convert it. Since Banana cannot convert to Apple, it fails. If Option Strict were on, you wouldn't even be able to compile it for that reason.
More specifically, the problem is your code makes it impossible to correctly proceed. It's as if you wrote:
Code:
Dim thisIsAnObjectApple As Apple
Public Property Apple() As IFruit
Get
Return thisIsAnObjectApple
End Get
Set(...)
thisIsAnObjectApple = value
End Set
End Property
This is impossible because I can write this:
Code:
something.Apple = New Banana()
Again: Banana is an IFruit, so it seems valid, but at runtime when VB figures out you gave it a Banana instead of an Apple it gets mad.
Personally, this is how I would've written clsTransaction:
Code:
Public Class Transaction
Implements ITransaction
Private _tags As IList(Of Tag)()
Public Property Tags() As IList(Of ITag)
Get
Return _tags
End Get
Set(ByVal value As IList(Of ITag))
_tags = value
End Set
End Property
End Class
But a lot has changed in VB since 2009, so this is REALLY how we'd write it today:
Code:
Public Class Transaction
Implements ITransaction
Public Property Tags() As IList(Of ITag)
End Class
A hierarchy is not needed, because IList(Of ITag) already provides the methods you use. You can assign List(Of ITag) to IList(Of ITag) because that is an "upcast". It's as if I wrote:
Code:
Public Property Fruit As IFruit
That can take EITHER an Apple or a Banana safely, because I'm only talking about them in terms of the shared interface IFruit.