Attributes, WithEvents, and Backing Fields
October 26th, 2004

Grahn Modulary in Kokomo, Indiana writes:

Why is only ‘booya’ printed out when Derived.Reflect() is called in this test program (below)? Shouldn’t I also get ‘raiser?’ What gives, Tito?

[Note, I omitted the ReflectMeAttribute and EventRaiser classes for clarity, they’re not important].


    Public Class Base

        <ReflectMe()> Protected WithEvents raiser As EventRaiser
        <ReflectMe()> Protected booya As String

        Protected asdf As String

        ' ...
    End Class

    Public Class Derived
        Inherits Base

        ''' <summary>
        ''' Prints out the name of all members marked with the ReflectMe() attribute.
        ''' </summary>
        Public Shared Sub Reflect()
           Dim members() As MemberInfo = GetType(Derived).GetMembers( _
               BindingFlags.NonPublic _
               Or BindingFlags.Public Or BindingFlags.Instance)

            For i As Integer = 0 To members.Length - 1
                If members(i).IsDefined(GetType(ReflectMeAttribute), True) Then
                    Console.WriteLine(members(i).Name)
                End If
            Next
        End Function
    End Class

  

Well Grahn, first of all, don’t call me “Tito.” Second, notice that if you move the Reflect method to the Base class, you’ll get two items back, but maybe not the ones you’d expect. You’ll get a private field called “_raiser.”

The problem originates with the use of the WithEvents keyword and the way it is handled by the VB compiler. The raiser field is replaced by a property of the same name, and a private backing field that together make the WithEvents work. The attribute is applied to the private field.

It’s a problem here because attributes will no longer work when inheritance is involved. You could possibly get around this if it were possible to target attributes in VB, but it’s not. This might fix it, as long as the attribute supports property targets:

<Property: ReflectMe()>

But currently the only attribute target you can specify this way in VB is Assembly.

Your example makes this look kind of lame, but one place this has bitten me in real code is in Page classes. When the ASP.NET 1.1 engine compiles a page, it derives from your codebehind class, thereby obscuring any private backing fields. It really gets crazy when you consider that the VS2003 designer will stick “WithEvents” on pretty much every field it generates, regardless of whether or not you use its functionality.

Where this REALLY becomes a problem is when you dabble in developing an attribute-based framework system for a web application (as I have). You’ll find yourself struggling to explain this early and often.

I understand that there are some changes to the way that pages are compiled in ASP 2.0 that would fix this situation, but to me it’s not attacking the real problem. If anyone sees this, let me know if you’re aware of an elegant way around it that I haven’t thought of.