The humidity in my office dipped below 20% this week, the same as last winter. If you need a frame of reference, the Sahara desert is usually at least 25%. Some people are able to tolerate this, but I’m apparently not one of them. Especially not when I’m in the 70-80 hour per week range. I have these questions:
- What are the long-term health detriments associated with being DRY?
- Would OSHA for New York City be likely to do anything about it being DRY?
- Is it socially acceptable to put chap stick on your eyelids?
- Seriously, what the f*ck?
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:
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.
Quiz for the day. This one highlights one of the many things I don’t like about VB. What’s the output of this program?
Class TestProgram
Private Shared Sub Main()
Console.WriteLine(CBool(Nothing))
Console.WriteLine(CBool(GetObject()))
Console.WriteLine(CBool(GetString()))
End Sub
Private Shared Function GetString() As String
Return Nothing
End Function
Private Shared Function GetObject() As Object
Return Nothing
End Function
End Class
Since it’s likely that nobody is reading this, I’ll tell you:
False
False
Unhandled Exception: System.InvalidCastException: Conversion from string "" to type 'Boolean' is not valid.
---> System.FormatException: Input string was not in a correct format.
Two developers I work with, one very experienced and one much newer, were both stuck on this kind of problem within a week of each other.
The sharp eyes out there probably caught on that the return type of the “get” function is what’s making the difference, which is true. But it’s certainly not obvious why that would be the case. After all, in all three cases you’re trying to convert null to a Boolean value. Looking at the MSIL gets us headed in the right direction:
.method public static void Main() cil managed
{
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
.entrypoint
// Code Size: 37 byte(s)
.maxstack 8
L_0000: ldc.i4.0
L_0001: call void [mscorlib]System.Console::WriteLine(bool)
L_0006: call object VbConsoleApp.TestProgram::GetObject()
L_000b: call bool [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions::ToBoolean(object)
L_0010: call void [mscorlib]System.Console::WriteLine(bool)
L_0015: call string VbConsoleApp.TestProgram::GetString()
L_001a: call bool [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions::ToBoolean(string)
L_001f: call void [mscorlib]System.Console::WriteLine(bool)
L_0024: ret
}
Notice:
- The conversion of the first literal just gets compiled out.
- The second two conversions are calls to different functions.
And herein lies one of the dangers of the VB conversion operators. A subtle change in what you give it can result in very different behavior. The ToBoolean(object) function will happily tell you that null==false, but the ToBoolean(string) function will disagree with a vengeance. The exception will also make it look like you gave it the empty string, when in fact you did not.
You’d probably see this if you swapped a Hashtable for a NameValueCollection, as an example. We saw it pulling the values of some checkboxes / hidden inputs out of a Request.Form collection. The other conversion operators work basically the same way, so I’m sure you’d see similar behavior.
If you think that this is nitpicky, consider that the System.Convert.ToBoolean(object) and System.Convert.ToBoolean(string) functions have consistent behavior when given null. I don’t think it’s too much to ask that CBool be held to the same standard.
When it’s my code, I avoid this stuff like the plague. These things are Thunderdome.