Best Practice Class Design

So I am trying to determine the best way to write a custom Class and make it searchable.

There can be multiple "FieldTemplates" classes, Each "FieldTemplates" Class will have items called "Fields", and could be alot. Each Field has the following values Name, Position, and Length.  Each  "FieldTemplates" belongs to a specific "Program".

This is what I started, but I am a little stumped if this is the best way for the design and I am having trouble searching for FieldNames, where the Criteria will most of the time need to equal both a FieldName and a Program, in order to return a specific "StartPosition" or "Length"

"Field Template" CLASS
Public Class FieldTemplates
    Implements IEquatable(Of FieldTemplates)
    
    Enum ProgType
        Prog1= 1  '0000001
        Prog2= 2    '0000010
        Prog3= 4  '0000100
        Prog4= 8    '0001000
    End Enum
  

    Private _Field As Field = New Field()
    Public Property Field As Field
        Get
            Return _Field
        End Get
        Set(ByVal value As Field)
            _Field = value
        End Set
    End Property

    Private _programType As ProgType
    Public Property ProgramType As ProgType
        Get
            Return _programType
        End Get
        Set(value As ProgType)
            _programType = value
        End Set
    End Property

    Private _programDirectory As String
    Public Property ProgramDirectory As String
        Get
            Return _programDirectory
        End Get
        Set(value As String)
            _programDirectory = value
        End Set
    End Property

    Public Function GetFieldPosition() As Integer
        Return _Field.Position
    End Function

    Public Function GetFieldLength() As Integer
        Return _Field.Length
    End Function


    Public Overrides Function Equals(obj As Object) As Boolean
        If obj Is Nothing Then
            Return False
        End If
        Dim objAsFieldTemplates As Field= TryCast(obj, FieldTemplates)
        If objAsFieldTemplates Is Nothing Then
            Return False
        Else
            Return Equals(objAsFieldTemplates)
        End If
    End Function

    Public Overrides Function GetHashCode() As Integer
        Return Field.Length
    End Function

    Public Overloads Function Equals(other As FieldTemplates) As Boolean _
        Implements IEquatable(Of FieldTemplates).Equals
        If other Is Nothing Then
            Return False
        End If
        Return (Me.Field.Equals(other.Field.FieldName))
    End Function
End Class

Open in new window


"FIELD" Class
Public Class FIELD
    Private _fieldName As String
    Public Property FieldName() As String
        Get
            Return _fieldName
        End Get
        Set(ByVal value As String)
            _fieldName = value
        End Set
    End Property

    Private _position As Integer
    Public Property Position() As Integer
        Get
            Return _position
        End Get
        Set(ByVal value As Integer)
            _position = value
        End Set
    End Property

    Private _length As Integer
    Public Property Length() As Integer
        Get
            Return _length
        End Get
        Set(ByVal value As Integer)
            _length = value
        End Set
    End Property
End Class

Open in new window



I add fields as follows:

Program.Add(New FieldTemplates() With _
                        {.Field = New Field() With {.FieldName = "Field1", _
                                                             .Position = 20, _
                                                             .Length = 30}, _
                        .ProgramType = ProgramField.ProgType.Prog3})

Open in new window

mcsdguyianAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

it_saigeDeveloperCommented:
Sounds like you need FieldTemplate to be implemented as a list of Field.  In this way you can make a single FieldTemplate represent a program with multiple fields.  For that, you would want to inherit List(Of Field); e.g. -
Imports EE_Q28819659

Module Module1
	ReadOnly templates As New List(Of FieldTemplate)

	Sub Main()
		templates.Add(New FieldTemplate(From i In Enumerable.Range(1, 5) Select New Field() With {.Name = String.Format("Field{0}", i), .Position = (i + 1) * 10, .Length = (i + 2) * 10}) With {.ProgramType = ProgramType.Prog1})
		templates.Add(New FieldTemplate(From i In Enumerable.Range(1, 5) Select New Field() With {.Name = String.Format("Field{0}", i), .Position = (i + 1) * 10, .Length = (i + 2) * 10}) With {.ProgramType = ProgramType.Prog2})
		templates.Add(New FieldTemplate(From i In Enumerable.Range(1, 5) Select New Field() With {.Name = String.Format("Field{0}", i), .Position = (i + 1) * 10, .Length = (i + 2) * 10}) With {.ProgramType = ProgramType.Prog3})
		templates.Add(New FieldTemplate(From i In Enumerable.Range(1, 5) Select New Field() With {.Name = String.Format("Field{0}", i), .Position = (i + 1) * 10, .Length = (i + 2) * 10}) With {.ProgramType = ProgramType.Prog4})

		Dim current = From template In templates
				    From field In template
				    Where template.ProgramType.Equals(ProgramType.Prog3) AndAlso field.Name.Equals("Field3")
				    Select New With {.Template = template, .Field = field}

		For Each item In current
			Console.WriteLine("Template: {0}; Field: {1}", item.Template, item.Field)
		Next
		Console.ReadLine()
	End Sub
End Module

Enum ProgramType
	Prog1 = 1  '0000001
	Prog2 = 2    '0000010
	Prog3 = 4  '0000100
	Prog4 = 8    '0001000
End Enum

Class FieldTemplate
	Inherits List(Of Field)
	Implements IEquatable(Of FieldTemplate)

	Public Property [ProgramType]() As ProgramType
	Public Property ProgramDirectory As String

	Public Sub New()
		MyBase.New()
	End Sub

	Public Sub New(ByVal capacity As Integer)
		MyBase.New(capacity)
	End Sub

	Public Sub New(ByVal source As IEnumerable(Of Field))
		MyBase.New(source)
	End Sub

	Public Overrides Function Equals(obj As Object) As Boolean
		If obj Is Nothing OrElse Not obj.GetType().Equals(Me.GetType()) Then
			Return False
		End If

		Return Me.Equals(DirectCast(obj, Field))
	End Function

	Public Overloads Function Equals(ByVal other As FieldTemplate) As Boolean Implements IEquatable(Of FieldTemplate).Equals
		If Object.ReferenceEquals(other, Nothing) Then
			Return False
		End If

		If other Is Me Then
			Return True
		End If

		Return Object.Equals(MyBase.ToList(), other.ToList()) AndAlso Object.Equals(ProgramType, other.ProgramType) AndAlso Object.Equals(ProgramDirectory, other.ProgramDirectory)
	End Function

	Public Overrides Function GetHashCode() As Integer
		Return MyBase.GetHashCode() Xor ProgramType.GetHashCode() Xor ProgramDirectory.GetHashCode()
	End Function

	Public Overrides Function ToString() As String
		Return String.Format("{0} in {1} contains {2} fields.", ProgramType, ProgramDirectory, Count)
	End Function

	Public Shared Operator =(ByVal lhs As FieldTemplate, ByVal rhs As FieldTemplate)
		Return Object.Equals(lhs, rhs)
	End Operator

	Public Shared Operator <>(ByVal lhs As FieldTemplate, ByVal rhs As FieldTemplate)
		Return Not lhs = rhs
	End Operator
End Class

Class Field
	Implements IEquatable(Of Field)
	Implements ICloneable

	Public Property Name() As String
	Public Property Position() As Integer
	Public Property Length() As Integer

	Public Sub New()

	End Sub

	''' <summary>Copy Constructor</summary>
	''' <param name="source"></param>
	Public Sub New(ByVal source As Field)
		Name = source.Name
		Position = source.Position
		Length = source.Length
	End Sub

	Public Overrides Function Equals(obj As Object) As Boolean
		If obj Is Nothing OrElse Not obj.GetType().Equals(Me.GetType()) Then
			Return False
		End If

		Return Me.Equals(DirectCast(obj, Field))
	End Function

	Public Overloads Function Equals(ByVal other As Field) As Boolean Implements IEquatable(Of Field).Equals
		If Object.ReferenceEquals(other, Nothing) Then
			Return False
		End If

		If other Is Me Then
			Return True
		End If

		Return Object.Equals(Name, other.Name) AndAlso Object.Equals(Position, other.Position) AndAlso Object.Equals(Length, other.Length)
	End Function

	Public Overrides Function GetHashCode() As Integer
		Return Name.GetHashCode() Xor Position.GetHashCode() Xor Length.GetHashCode()
	End Function

	Public Function Clone() As Object Implements ICloneable.Clone
		Return New Field(Me)
	End Function

	Public Overrides Function ToString() As String
		Return String.Format("Name: {0}; Position: {1}; Length: {2}", Name, Position, Length)
	End Function

	Public Shared Operator =(ByVal lhs As Field, ByVal rhs As Field)
		Return Object.Equals(lhs, rhs)
	End Operator

	Public Shared Operator <>(ByVal lhs As Field, ByVal rhs As Field)
		Return Not lhs = rhs
	End Operator
End Class

Open in new window

Which produces the following output -Capture.JPG-saige-
0
mcsdguyianAuthor Commented:
Thanks, That looks like it will work.  I am not very familiar with using LINQ and I am trying to add values without using the Enumerable.Range like your example, For example I want to Add
CompanyName, Address, City, State, Zip.

I use the following code:
templates.Add(New FieldTemplate(New Field() With {.Name = "CompanyName", .Position = 91, .Length = 30}) With {.ProgramType = ProgramType.MiscGage})

Open in new window


I get the following Error:
Unable to cast object of type 'Testing_Template.Field' to type 'System.Collections.Generic.IEnumerable`1[Testing_Template.Field]'.

Open in new window


What I will need to do is assign a lot of variables the values from the Template class at any one time and I don't know if I will be able to do that with LINQ properly is there a lookup to use.

I would like to also be able to assign the values as follows:
Field1Name_Position = 91
Field1Name_Length = 30

Field2Name_Position = 121
Field2Name_Length = 30

Field3Name_Position = 121
Field3Name_Length = 30

Open in new window

templates.Add(New FieldTemplate(New Field() With {.Name = "CompanyName", .Position = 91, .Length = 30}) With {.ProgramType = ProgramType.Prog1})

Open in new window

0
it_saigeDeveloperCommented:
You get the error because the constructor for FieldTemplate accepts either an integer or an Enumerable(Of Field).  In order to add the FieldTemplate with a single field, you need to use collection initialization syntax; e.g. -
templates.Add(New FieldTemplate() From {New Field() With {.Name = "CompanyName", .Position = 91, .Length = 30}})

Open in new window

Unfortunately, you cannot combine collection initialization syntax with object initialization syntax, which would mean that the following is invalid syntax:
templates.Add(New FieldTemplate() From {New Field() With {.Name = "CompanyName", .Position = 91, .Length = 30}} With {.ProgramType = ProgramType.Prog4})

Open in new window

To combat this, we could do any of the following:
A.  Use array initialization with our existing contructor combined with object initialization syntax; e.g. -
templates.Add(New FieldTemplate({New Field() With {.Name = "CompanyName", .Position = 91, .Length = 30}}) With {.ProgramType = ProgramType.Prog3})

Open in new window

B.  Create an instance of the class and set the properties prior to adding it to the list; e.g. -
' Extremely verbose
Dim template As New FieldTemplate()
template.ProgramType = ProgramType.Prog3
template.Add(New Field() With {.Name = "CompanyName", .Position = 91, .Length = 30})
templates.Add(template)

Open in new window

C.  Implement constructors that accept the fields/properties we want to set the values for; e.g. -
Class FieldTemplate
	Inherits List(Of Field)
	Implements IEquatable(Of FieldTemplate)

	Public Property [ProgramType]() As ProgramType
	Public Property ProgramDirectory As String

	Public Sub New()
		MyBase.New()
	End Sub

	Public Sub New(ByVal capacity As Integer)
		MyBase.New(capacity)
	End Sub

	Public Sub New(ByVal source As IEnumerable(Of Field))
		MyBase.New(source)
	End Sub

	Public Sub New(ByVal [ProgramType] As ProgramType, ByVal capacity As Integer)
		Me.New([ProgramType], String.Empty, capacity)
	End Sub

	Public Sub New(ByVal [ProgramType] As ProgramType, ByVal source As IEnumerable(Of Field))
		Me.New([ProgramType], String.Empty, source)
	End Sub

	Public Sub New(ByVal [ProgramType] As ProgramType, ByVal ProgramDirectory As String, ByVal capacity As Integer)
		MyBase.New(capacity)
		Me.ProgramType = [ProgramType]
		Me.ProgramDirectory = ProgramDirectory
	End Sub

	Public Sub New(ByVal [ProgramType] As ProgramType, ByVal ProgramDirectory As String, ByVal source As IEnumerable(Of Field))
		MyBase.New(source)
		Me.ProgramType = [ProgramType]
		Me.ProgramDirectory = ProgramDirectory
	End Sub

	Public Overrides Function Equals(obj As Object) As Boolean
		If obj Is Nothing OrElse Not obj.GetType().Equals(Me.GetType()) Then
			Return False
		End If

		Return Me.Equals(DirectCast(obj, Field))
	End Function

	Public Overloads Function Equals(ByVal other As FieldTemplate) As Boolean Implements IEquatable(Of FieldTemplate).Equals
		If Object.ReferenceEquals(other, Nothing) Then
			Return False
		End If

		If other Is Me Then
			Return True
		End If

		Return Object.Equals(MyBase.ToList(), other.ToList()) AndAlso Object.Equals(ProgramType, other.ProgramType) AndAlso Object.Equals(ProgramDirectory, other.ProgramDirectory)
	End Function

	Public Overrides Function GetHashCode() As Integer
		Return MyBase.GetHashCode() Xor ProgramType.GetHashCode() Xor ProgramDirectory.GetHashCode()
	End Function

	Public Overrides Function ToString() As String
		Return String.Format("{0} in {1} contains {2} fields.", ProgramType, ProgramDirectory, Count)
	End Function

	Public Shared Operator =(ByVal lhs As FieldTemplate, ByVal rhs As FieldTemplate)
		Return Object.Equals(lhs, rhs)
	End Operator

	Public Shared Operator <>(ByVal lhs As FieldTemplate, ByVal rhs As FieldTemplate)
		Return Not lhs = rhs
	End Operator
End Class

'Now we can add it using
templates.Add(New FieldTemplate(ProgramType.Prog3) From {New Field() With {.Name = "CompanyName", .Position = 91, .Length = 30}})

Open in new window

-saige-
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

mcsdguyianAuthor Commented:
it_saige, I appreciate your help and insight.

What ways would you say would be the best way to search and return all the Fields from the FieldTemplate class where for example ProgramType = ProgramType.Prog3?

Can you show me a LINQ and non-LINQ example that would be effective to assign around 20 field values (Length, Position) from the FieldTemplate class to local variables. Looking at the LINQ example you provided I am not sure how I would accomplish this.

Thanks
0
it_saigeDeveloperCommented:
First realize that fieldtemplate *is* a collection of fields, so you only have to return the fieldtemplate that matches the program type; e.g. -

Using LINQ -
Dim selected = From template In templates
		    Where template.ProgramType.Equals(ProgramType.Prog3)
		    Select template

Open in new window


Using For Each Loop -
Dim selected As New List(Of FieldTemplate)
For Each [template] In templates
	If [template].ProgramType.Equals(ProgramType.Prog3) Then
		selected.Add(template)
	End If
Next

Open in new window


Using a For Loop -
Dim selected As New List(Of FieldTemplate)
For i As Integer = 0 To templates.Count - 1 Step 1
	If templates(i).ProgramType.Equals(ProgramType.Prog3) Then
		selected.Add(templates(i))
	End If
Next

Open in new window


Proof of concept -
Module Module1
	ReadOnly templates As New List(Of FieldTemplate)

	Sub Main()
		templates.Add(New FieldTemplate(From i In Enumerable.Range(1, 5) Select New Field() With {.Name = String.Format("Field{0}", i), .Position = (i + 1) * 10, .Length = (i + 2) * 10}) With {.ProgramType = ProgramType.Prog1})
		templates.Add(New FieldTemplate(From i In Enumerable.Range(1, 5) Select New Field() With {.Name = String.Format("Field{0}", i), .Position = (i + 1) * 10, .Length = (i + 2) * 10}) With {.ProgramType = ProgramType.Prog2})
		templates.Add(New FieldTemplate(From i In Enumerable.Range(1, 5) Select New Field() With {.Name = String.Format("Field{0}", i), .Position = (i + 1) * 10, .Length = (i + 2) * 10}) With {.ProgramType = ProgramType.Prog3})
		templates.Add(New FieldTemplate(From i In Enumerable.Range(1, 5) Select New Field() With {.Name = String.Format("Field{0}", i), .Position = (i + 1) * 10, .Length = (i + 2) * 10}) With {.ProgramType = ProgramType.Prog4})
		templates.Add(New FieldTemplate({New Field() With {.Name = "CompanyName", .Position = 91, .Length = 30}}) With {.ProgramType = ProgramType.Prog3})

		' Example of LINQ usage to find ProgramType Prog3
		'Dim selected = From template In templates
		'		    Where template.ProgramType.Equals(ProgramType.Prog3)
		'		    Select template

		' Example of For Each Loop usage to find ProgramType Prog3
		'Dim selected As New List(Of FieldTemplate)
		'For Each [template] In templates
		'	If [template].ProgramType.Equals(ProgramType.Prog3) Then
		'		selected.Add(template)
		'	End If
		'Next

		' Example of For Loop usage to find ProgramType Prog3
		Dim selected As New List(Of FieldTemplate)
		For i As Integer = 0 To templates.Count - 1 Step 1
			If templates(i).ProgramType.Equals(ProgramType.Prog3) Then
				selected.Add(templates(i))
			End If
		Next

		For Each [template] In selected
			For Each [field] In [template]
				Console.WriteLine("Template: {0}; Field: {1}", [template], [field])
			Next
		Next

		Console.ReadLine()
	End Sub
End Module

Enum ProgramType
	Prog1 = 1	 '0000001
	Prog2 = 2	   '0000010
	Prog3 = 4	 '0000100
	Prog4 = 8	   '0001000
End Enum

Class FieldTemplate
	Inherits List(Of Field)
	Implements IEquatable(Of FieldTemplate)

	Public Property [ProgramType]() As ProgramType
	Public Property ProgramDirectory As String

	Public Sub New()
		MyBase.New()
	End Sub

	Public Sub New(ByVal capacity As Integer)
		MyBase.New(capacity)
	End Sub

	Public Sub New(ByVal source As IEnumerable(Of Field))
		MyBase.New(source)
	End Sub

	Public Overrides Function Equals(obj As Object) As Boolean
		If obj Is Nothing OrElse Not obj.GetType().Equals(Me.GetType()) Then
			Return False
		End If

		Return Me.Equals(DirectCast(obj, Field))
	End Function

	Public Overloads Function Equals(ByVal other As FieldTemplate) As Boolean Implements IEquatable(Of FieldTemplate).Equals
		If Object.ReferenceEquals(other, Nothing) Then
			Return False
		End If

		If other Is Me Then
			Return True
		End If

		Return Object.Equals(MyBase.ToList(), other.ToList()) AndAlso Object.Equals(ProgramType, other.ProgramType) AndAlso Object.Equals(ProgramDirectory, other.ProgramDirectory)
	End Function

	Public Overrides Function GetHashCode() As Integer
		Return MyBase.GetHashCode() Xor ProgramType.GetHashCode() Xor ProgramDirectory.GetHashCode()
	End Function

	Public Overrides Function ToString() As String
		Return String.Format("{0} in {1} contains {2} fields.", ProgramType, ProgramDirectory, Count)
	End Function

	Public Shared Operator =(ByVal lhs As FieldTemplate, ByVal rhs As FieldTemplate)
		Return Object.Equals(lhs, rhs)
	End Operator

	Public Shared Operator <>(ByVal lhs As FieldTemplate, ByVal rhs As FieldTemplate)
		Return Not lhs = rhs
	End Operator
End Class

Class Field
	Implements IEquatable(Of Field)
	Implements ICloneable

	Public Property Name() As String
	Public Property Position() As Integer
	Public Property Length() As Integer

	Public Sub New()

	End Sub

	''' <summary>Copy Constructor</summary>
	''' <param name="source"></param>
	Public Sub New(ByVal source As Field)
		Name = source.Name
		Position = source.Position
		Length = source.Length
	End Sub

	Public Overrides Function Equals(obj As Object) As Boolean
		If obj Is Nothing OrElse Not obj.GetType().Equals(Me.GetType()) Then
			Return False
		End If

		Return Me.Equals(DirectCast(obj, Field))
	End Function

	Public Overloads Function Equals(ByVal other As Field) As Boolean Implements IEquatable(Of Field).Equals
		If Object.ReferenceEquals(other, Nothing) Then
			Return False
		End If

		If other Is Me Then
			Return True
		End If

		Return Object.Equals(Name, other.Name) AndAlso Object.Equals(Position, other.Position) AndAlso Object.Equals(Length, other.Length)
	End Function

	Public Overrides Function GetHashCode() As Integer
		Return Name.GetHashCode() Xor Position.GetHashCode() Xor Length.GetHashCode()
	End Function

	Public Function Clone() As Object Implements ICloneable.Clone
		Return New Field(Me)
	End Function

	Public Overrides Function ToString() As String
		Return String.Format("Name: {0}; Position: {1}; Length: {2}", Name, Position, Length)
	End Function

	Public Shared Operator =(ByVal lhs As Field, ByVal rhs As Field)
		Return Object.Equals(lhs, rhs)
	End Operator

	Public Shared Operator <>(ByVal lhs As Field, ByVal rhs As Field)
		Return Not lhs = rhs
	End Operator
End Class

Open in new window


Which produces the following output -Capture.JPG-saige-
0
mcsdguyianAuthor Commented:
saige, Your solution is perfect and worked like a charm.

I am curious I also added a datatable to my FieldTemplate Class to store multiple row of data where there could be up to 10 columns in each row and there could be dozens of rows storing data, that very per class instance.

After see how you recommended "Inherits List(Of Field)" for the FieldTemplate Class.  I am wondering if I should drop the datatable and create another class similar to the Field Class and use "Inherits List(Of RowData)". What do you think?
0
mcsdguyianAuthor Commented:
Excellent work.  Thanks for your help!
0
it_saigeDeveloperCommented:
Not a problem, glad I could be of assistance.

-saige-
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.