Tuesday, March 15, 2011

Problem declaring an anonymous method with vb.net Action(Of T) and lambda.

Imports System.Reflection
Public Class Test
    Private Field As String
End Class

Module Module1
    Sub Main()

        Dim field = GetType(Test).GetField("Field", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)

        Dim test = New Test

        Dim GetValue = New Func(Of Test, String)(Function(t As Test) field.GetValue(test))

        'This line indicates a compile error: 'Expression does not produce a value':
        Dim SetValue = New Action(Of Test, String)(Function(t As Test, value As String) field.SetValue(test, value))
    End Sub
 End Module


Module Module2
    Dim field = GetType(Test).GetField("Field", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance) 'Is Shared (Module)
    Sub Main2()
        Dim test = New Test
        Dim GetValue = New Func(Of Test, String)(Function(t As Test) field.GetValue(test))
        Dim SetValue = New Action(Of Test, String)(Function(t As Test, value As String) field.SetValue(test, value))
    End Sub
End Module

Donno what's wrong but Module2 works just great!

From stackoverflow
  • EDIT Scratch my original answer, I misread the problem.

    The reason this does not compile is an issue of type inference and late binding. In the first example field is a local variable and hence can participate in type inference. The compiler will correctly deduce the type to be FieldInfo. This means the SetValue call is a statically typed call. It is a void returning method and is hence incompatible with a Function lambda expression which requires a return value.

    The field value in the second example though is declared at a module level. These variables are not subject to type inference and hence the type object will be chosen. Since the type is object, the SetValue call becomes a late bound call. All late bound calls are assumed to point to a function that has a return type of Object. At runtime if the function returns void, Nothing will actually be returned. So in this context it is a non-void returning expression and hence compiles.

    One option you have to work around this is to explicitly type field as Object in the first example. This will force it to be a late bound call and it will compile just like the second one

    Dim field As Object = ...
    
    Nathan W : why does the second one work fine?
    Shimmy : donno, it does try and see
    JaredPar : @Nathan, I misread the problem and updated my answer.
    Nathan W : That sounds a bit better +1
    Shimmy : I prefer to cast it, because I need it in more cases where I use it directly. look at the post.
    Shimmy : while enjoying from intellisense support around.
  • Well here is the final answer based on JaredPar's post:

    Module Module1
        Sub Main()
            Dim field = GetType(Test).GetField("Field", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
            Dim test = New Test
            Dim GetValue = New Func(Of Test, String)(Function(t As Test) field.GetValue(test))
            'This line indicates a compile error: 'Expression does not produce a value': 
            Dim SetValue = New Action(Of Test, String)(Function(t As Test, value As String) DirectCast(field, Object).SetValue(test, value))
        End Sub
    End Module
    

    Notice the cast to object at

    Dim SetValue = New Action(Of Test, String)(Function(t As Test, value As String) DirectCast(field, Object).SetValue(test, value))
    

0 comments:

Post a Comment