Reputation: 113
I am trying to create a class with a shared function that will return a class based on the value of a parameter, instead of a class instance (basically a class factory.) Then, I will call the class' constructor to get the instance I need.
For example, I have these three classes:
class Test
sub New(byval anID as Integer)
end sub
end class
class Test_A
inherits Test
sub New(byval anID as Integer)
end sub
end class
class Test_B
inherits Test
sub New(byval anID as Integer)
end sub
end class
I want something like (I know it does not work):
class TestFactory
shared function Create(byval aParam as Integer) as Test
select aParam
case 0
return Test_A
case 1
return Test_B
...
End Select
end function
end class
to later use it in the following way:
dim aTest as Test = New TestFactory.Create(0)(anID:=100)
I searched for similar question, and they all seem to suggest using generics. But in my case i think they are not suitable, since I need to know the class beforehand (see this answer.)
Any suggestions?
Upvotes: 0
Views: 175
Reputation: 415735
I was surprised that I couldn't make this work with the constructor directly, but you can at least return a delegate/lambda expression that calls into the constructor you want:
Class TestFactory
Public Shared Function Create(byval aParam as Integer) as Func(Of Integer, Test)
Select aParam
Case 0
return Function(id) New Test_A(id)
Case 1
Return Function(id) New Test_B(id)
...
End Select
End Function
End Class
And then you can call it almost exactly as you described:
Dim aTest As Test = TestFactory.Create(0)(100)
What we are doing now is effectively Currying your Test factory.
My experience, though, is that this is the wrong direction in the first place. Create()
methods generally return instances, not Types, and right or wrong, there will be an expectation for this among other programmers who use the type and are familiar with the pattern. If you do go down this path, at least consider a different name for the method.
Upvotes: 2
Reputation: 8160
In the example case, you could just pass in your constructor parameter to your factory method:
class TestFactory
shared function Create(byval aParam as Integer, byval anID as Integer) as Test
select aParam
case 0
return New Test_A(anID)
case 1
return New Test_B(anID)
...
End Select
end function
end class
Otherwise, I think you would have to return a Type
and call the constructor using reflection, which is not so nice because, as mentioned in the comments, the method signature does not guarantee that the returned type is a subtype of Test
.
class TestFactory
shared function Create(byval aParam as Integer) as Type
select aParam
case 0
return GetType(Test_A)
case 1
return GetType(Test_B)
...
End Select
end function
end class
' Use something like this...
Dim t As Type = TestFactory.Create(0)
' Probably need a DirectCast here...
Dim myTest As Test = t.GetConstructor(New Type() {GetType(Integer)}) _
.Invoke(New Object() {anID})
Upvotes: 1