3

I'm having some difficulties using generic types. We have a deserialize method which signature looks like this:

Public Function Deserialize(Of T)(ByVal compressedData As Byte()) As T

We have some binary data in database (which can be several types: type1.Question, type2.Question,...). So to prevent having to Select case in my loop, I tried something like this:

Dim questionType as Type = question.getType()
Deserialize(Of questionType)(question)

This type is not recognized : "Type 'questionType' is not defined"

Is there any way I can achieve this?

I have read and tried Suggestion 1 and Suggestion 2, but for case 1, I cannot use type T as an expression and in case 2, I cannot use "Of type" as that gives in error in the code (the "type is not defined" one).

Community
  • 1
  • 1
Terry
  • 5,132
  • 4
  • 33
  • 66
  • Is Deserialize(Of T) a member of a generic class, or just a random function? – tcarvin Mar 01 '13 at 13:09
  • @tcarvin: it's a random function. – Terry Mar 01 '13 at 13:10
  • There are things you can do but unless you don't know the type till runtime, its faster and easier to do the case statement as you suggest. If you don't know the type till runtime you'll end up having to use http://msdn.microsoft.com/en-gb/library/system.type.makegenerictype.aspx – Jodrell Mar 08 '13 at 11:04

2 Answers2

3

AFAIK, you cannot dynamically do that (without perhaps reflection and emit) as the compiler has to know what type to generate code for.

I typically use a dictionary keyed by the Type with the value being the handler objects. Even though you don't have a Deserializer object, could store a delegate to the generic function instead.

Something like this (from the hip so should be checked):

'register these just once
_deserializers.Add(GetType(QuestionStyleOne), AddressOf(Deserialize(QuestionStyleOne)())

'later invoke by type, no select case
Dim questionType as Type = question.getType()
_deserializers(questionType)(question)
tcarvin
  • 10,715
  • 3
  • 31
  • 52
  • Hmmm, the answer in http://stackoverflow.com/questions/232535/how-to-use-reflection-to-call-generic-method shows you can use reflection to achieve this without pre-registering the types. You should read that answer again. – tcarvin Mar 01 '13 at 13:43
  • +1. I like your type dictionary option over any reflection-based approach. – Victor Zakharov Mar 01 '13 at 14:45
  • What does the dictionary look like? – Terry Mar 01 '13 at 14:53
  • It would have to be `Dim deserializeFns As New Dictionary(Of Type, [Delegate])` in the scenario you described, but it occurs to me that the real issue is that the QuestionType isn't a `Type` at all but rather a string or an enum in your database isn't it? – tcarvin Mar 01 '13 at 17:21
  • Your answer didn't fully work for me, but it put me in the right direction. – Terry Mar 08 '13 at 10:48
1

I finally worked out a solution for this problem. Starting with the suggestion from tcarvin, I changed some things around and got to this solution:

I made a separate helper class, with a shared dictionary:

Private Shared _QuestionStyles As New Dictionary(Of Type, Func(Of Byte(), QuestionBase)) From {
    {GetType(QuestionStyleOne), Function(questionBinary)
                                                Return Deserialize(Of QuestionStyleOne)(questionBinary)
                                            End Function},
    {GetType(QuestionStyleTwo), Function(questionBinary)
                                                Return Deserialize(Of QuestionStyleTwo)(questionBinary)
                                            End Function}
}

In that same class, I made a method which tries to retrieve to correct function stored in the dictionary:

Public Shared Function DoDeserializeQuestion(ByVal questionType As Type, ByVal questionBinary As Byte())
    Dim deserializeQuestion As Func(Of Byte(), QuestionBase) = Nothing
    If (_QuestionStyles.TryGetValue(questionType, deserializeQuestion)) Then
        Return deserializeQuestion(questionBinary)
    Else
        Throw New ArgumentException("QuestionType not known")
    End If
End Function

Now I only needed to call for this:

Using reader As IDataReader = GetQuestions(id)
    While (reader.Read) 
        collection.Add(QuestionTypeHelper.DoDeserializeQuestion(question.GetType, CType(reader.Item(DataFields.Question), Byte())))
    End While
End Using

This gets me the right questions from the DB, deserializes that binary and cast them to the appropriate question type. Very reusable in other cases and where that type of casting is needed.

Terry
  • 5,132
  • 4
  • 33
  • 66