OK, quick quiz. And, yes, this is a trick question. Without looking at VS, what do you expect the following code to print?
Public Sub Foo()
Dim s1, s2 As String
s1 = "abc"
s2.Copy(s1)
Console.WriteLine(s2)
End Sub
Yes, that’s right, it prints nothing. Wha….? Because String.Copy
is a shared function that takes a string and returns a copy of it. It has no effect on the instance that it’s being called on.
Another quick quiz. And, yes, this is also a trick question. Without looking at VS, what do you expect the following code to print?
Public Class Foo
Public Shared Baz As Integer = 10
End Class
Module Test
Public Function Bar() As Foo
Console.WriteLine("abc")
Return New Foo()
End Function
Sub Main()
Dim y As Integer
y = Bar().Baz
End Sub
End Module
Yes, that’s right, it prints nothing. Wha….? Since Baz
is a shared member, we do not need to evaluate the qualifying instance expression, so we don’t. So Bar
is never called.
In general, it is a bad idea to call shared members off of instance expressions for these two reasons. Shared members cannot affect instances directly, so calling shared methods or changing shared properties/fields off of an instance may give surprising results. And because we do not evaluate expressions that qualify shared members, you won’t get side effects where you might expect it. (This has bitten even a few members of the VB team themselves.)
A legitimate question then is, if it’s a bad idea, why do we allow it in the first place? Unfortunately, the original logic has been lost to history – my grey matter no longer contains the memory of when and why we decided to allow this. One possible reason is that disallowing shared member access off of an instance expression causes problems in a common situation:
Enum Color
Red
Blue
Green
End Enum
Class Control
Public Color As Color = Color.Red
End Control
With just the simple rule that shared members cannot be accessed off of instances, the field declaration in Control would be in error. This is because the Color
in Color.Red
will bind first to the field Control.Color
, which is an instance field. Which means you can’t access shared members off of it, and enum members are shared. C# fixes this problem by introducing a special rule: in a context where the name of a variable is the same as the name of its type, you can access shared instances off of an instance. As it turns out, we had to introduce this rule anyway late in VS 2002 for the following related situation:
Class Control
Public Color As Color
Public Shared Sub Bar(ByVal SomeControl As Control)
SomeControl.Color = Color.Red
End Sub
End Class
In this case, the Color
in Color.Red
still binds to the instance field, but you run afoul of the rule that says that you can’t access instance members in a shared context.
Whatever the original logic, it’s become clear that this is not a good design and that we made a mistake in allowing it. (Yes, Virginia, we do make occasionally make mistakes around here.) I encourage people to avoid using it whenever possible. Unfortunately, the compiler doesn’t really help you on this today, but it’s something we’re be helping you more with in the future.
I thought all string functions always returns new instance of the string as strings are immutable…
True, but not everyone knows that… 🙂