Accessing shared members from instances

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.

2 thoughts on “Accessing shared members from instances

Leave a Reply

Your email address will not be published. Required fields are marked *