Results 1 to 11 of 11

Thread: Inheriting a generic Class based on Generic Interface

  1. #1

    Thread Starter
    New Member
    Join Date
    Nov 2009
    Location
    UK
    Posts
    6

    Question 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?

    GenericInterfaces.zip
    Last edited by simrim1; Feb 13th, 2018 at 11:46 AM.

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    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.

  3. #3

    Thread Starter
    New Member
    Join Date
    Nov 2009
    Location
    UK
    Posts
    6

    Red face 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

  4. #4

    Thread Starter
    New Member
    Join Date
    Nov 2009
    Location
    UK
    Posts
    6

    Unhappy 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.

  5. #5

    Thread Starter
    New Member
    Join Date
    Nov 2009
    Location
    UK
    Posts
    6

    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
    Last edited by simrim1; Feb 13th, 2018 at 11:48 AM.

  6. #6
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: Inheriting a generic Class based on Generic Interface

    You STILL haven't told us what line throws that exception.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  7. #7
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    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.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  8. #8
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    3,201

    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

  9. #9

    Thread Starter
    New Member
    Join Date
    Nov 2009
    Location
    UK
    Posts
    6

    Re: Inheriting a generic Class based on Generic Interface

    Quote Originally Posted by Sitten Spynne View Post
    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.

  10. #10

    Thread Starter
    New Member
    Join Date
    Nov 2009
    Location
    UK
    Posts
    6

    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.

  11. #11
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    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.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width