Angelos Arampatzis
Angelos Arampatzis

Reputation: 113

Is it possible to have a function return a class instead of an object?

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

Answers (2)

Joel Coehoorn
Joel Coehoorn

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

Mark
Mark

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

Related Questions