views:

121

answers:

4

Hello, I am really trying to follow the DRY principle. I have a sub that looks like this?

Private Sub DoSupplyModel

        OutputLine("ITEM SUMMARIES")
        Dim ItemSumms As New SupplyModel.ItemSummaries(_currentSupplyModel, _excelRows)
        ItemSumms.FillRows()
        OutputLine("")

        OutputLine("NUMBERED INVENTORIES")
        Dim numInvs As New SupplyModel.NumberedInventories(_currentSupplyModel, _excelRows)
        numInvs.FillRows()
        OutputLine("")   

End Sub

I would like to collapse these into a single method using generics. For the record, ItemSummaries and NumberedInventories are both derived from the same base class DataBuilderBase.

I can't figure out the syntax that will allow me to do ItemSumms.FillRows and numInvs.FillRows in the method.

FillRows is declared as Public Overridable Sub FillRows in the base class.

Thanks in advance.

EDIT
Here is my end result

Private Sub DoSupplyModels()

    DoSupplyModelType("ITEM SUMMARIES",New DataBlocks(_currentSupplyModel,_excelRows)
    DoSupplyModelType("DATA BLOCKS",New DataBlocks(_currentSupplyModel,_excelRows)

End Sub

Private Sub DoSupplyModelType(ByVal outputDescription As String, ByVal type As DataBuilderBase)
    OutputLine(outputDescription)
    type.FillRows()
    OutputLine("")
End Sub

But to answer my own question...I could have done this...

Private Sub DoSupplyModels()

    DoSupplyModelType(Of Projections)("ITEM SUMMARIES")
    DoSupplyModelType(Of DataBlocks)("DATA BLOCKS")

End Sub

Private Sub DoSupplyModelType(Of T as DataBuilderBase)(ByVal outputDescription As String, ByVal type As T)
    OutputLine(outputDescription)
    Dim type as New DataBuilderBase (_currentSupplyModel,_excelRows)
    type.FillRows()
    OutputLine("")
End Sub

Is that right?

Seth

+1  A: 

you can refactor to take advantage of the fact that the share a common base and use polymorphism: (VB a bit rusty, you should get the idea)

you could have a method:

Private Sub FillAndOutput(textToOutput as String, filler as DataBuilderBase)
    OutputLine(string)        
    filler.FillRows()
    OutputLine("")
end sub

which you could call:

Private Sub DoSupplyModel
    FillAndOutput("ITEM SUMMARIES",New SupplyModel.ItemSummaries(_currentSupplyModel, _excelRows))
    FillAndOutput("NUMBERED INVENTORIES",New SupplyModel.NumberedInventories(_currentSupplyModel, _excelRows))        

End Sub
Sam Holder
I think that's already true **FillRows is declared as Public Overridable Sub FillRows in the base class.**
R0MANARMY
ahh thanks, edited the examples to reflect that
Sam Holder
+1  A: 

Basically you need to specify that T will be a subclass of the base type that implements FillRows method. In C# this would look like so

private void myFunction<T>( T someList ) where T : DataBuilderBase {
    someList.FillRows();
}

Found a VB.NET example on MSDN.

EDIT and Kevin is right, this would probably be handled better with polymorphism.

R0MANARMY
It is that where T : DataBuilderBase that I did not understand how to do. (In other words how to constrain the generic type.) In VB the syntax is Private Sub myFunction(Of T As DataBuilderBase)(ByVal someList As T). THANKS A BILLION.
Seth Spearman
A: 

In this case I don't think that refactoring to a generic function is justifiable, even at the expense of repeating yourself. You would have two options:

  1. Refactor your code so that it allows for parameterless constructors and create a function at a higher level of inheritance that allows you to specify the arguments currently being passed to the constructor.
  2. Use explicit reflection to instantiate the generic type and pass those arguments to the constructor.

Either of those options involves a non-trivial amount of work with very little gain (you're translating three lines of code into one).

However, to answer your question, VB.NET uses the of keyword to specify generic type arguments

Public Sub Foo(Of T)(argument as T)
   ...
End Sub
Adam Robinson
I am curious why you don't think this is justifiable? Seth
Seth Spearman
@Seth: The cost of making it generic is too high compared with the benefits you'll see from it. The only reason you'd need to make it generic would be either to instantiate the generic type (which would mean you'd have to go through the trouble I listed in my anwer) or to return the generic type so that you could have a more fluent interface (I don't really see this being the case). The only portion that could be refactored out apart from these is the call to `FillRows`, and I just don't see the benefit in creating a new function that simply calls one other function with no logic.
Adam Robinson
+2  A: 

As others have pointed out, you don't need generics to do what you want, but I will answer the technical question for completeness:

Private Sub MyMethod(Of T As DataBuilderBase)(ByVal instance As T)
    instance.FillRows()
End Sub

And then call the method by doing this:

MyMethod(Of ItemSummaries)(new SupplyModel.ItemSummaries(...))
Jon Seigel
Thanks for actually answering the question in the right language.
R0MANARMY