I've noticed some seemingly weird issues in Visual Studio 2008 (.NET 3.5) and also in Visual Studio 2010 Beta 2 (.NET 4.0). These issues may have existed in prior versions as well. Maybe they are not an issue, but either way, I would like to see if there is are logical explanations for these before I submit a report on Microsoft Connect.
The Setup (in VB, C# results differ and are included later in the post):
Public Class SomeClass
Public Property SomeProperty() As String
Get
Return String.Empty
End Get
Set(ByVal value As String)
End Set
End Property
End Class
Public Class SomeOtherClass
Public Sub New()
Dim sc As New SomeClass()
Me.SomeFunction(sc.SomeProperty)
End Sub
''' <summary>The param as Object fn()</summary> '''
Public Sub SomeFunction(ByVal param As Object)
End Sub
''' <summary>The param as T fn()</summary> '''
Public Sub SomeFunction(Of T)(ByRef param As T)
End Sub
End Class
In this sutation, the Me.SomeFunction(sc.SomeProperty)
call, from the point of view of IntelliSense, looks like this:
So, I guess the first question I have is, why was the ByRef templated overload version of the function chosen over the ByVal Object overload version of the function? My guess is that the compiler and IntelliSense simply favor the templated versions over non-templated versions. At runtime, it is in fact the ByRef templated version of the function that is called. (This is not the defect, this is simply a personal want-to-know question.)
Now, make a slight change to the SomeProperty
property such that the setter is now private:
Public Property SomeProperty() As String
Get
Return String.Empty
End Get
Private Set(ByVal value As String)
End Set
End Property
As soon as you do this, the following happens to the Me.SomeFunction(sc.SomeProperty)
line:
In this case, IntelliSense is suggesting that the ByVal Object overload version of the function is being called, however the error message is the 'Set' accessor of property 'SomeProperty' is not accessible
indicating that the compiler is still expecting to call the ByRef templated version. So, this is my second question. Why is Intellisense claiming one thing while the VB compiler is clearly trying something else? This seems broken to me. Or am I missing something?
If instead of having a private setter on SomeProperty
but instead the property was simply marked ReadOnly and the setter part removed, then the ByRef templated version of the function is shown in IntelliSense and is called at runtime (with no runtime errors). So this leads me to my third question, why is the VB compiler treating the input to ByRef params different for properties that are ReadOnly vs not-ReadOnly, but have an out-of-scope setter in VB As far as SomeFunction(Of T)(...) is concerned, in its current scope, that property should be as if it were ReadOnly and I would expect it to be callable just as if the property was in fact ReadOnly. But instead it produces a build error.
In correlation with question three, doing the exact same setup (with the Private setter), C# has the result I was expecting.
Here, you can see IntelliSense is claiming t开发者_开发知识库hat the SomeFunction(Object) overload of the function is being called and there is no build error. At runtime the SomeFunction(Object) version is in fact called. So, why isn't the same SomeFunction(Object) version being called in the VB.NET situation? Why does VB.NET still think the SomeFunction(Of T)(ByRef T) version need to be called? It looks like IntelliSense is nailing it correctly in both C# and VB.NET, the C# compiler is doing the right thing, but the VB.NET compiler is still convinced that it should be calling the ByRef templated version. To me it seems that the C# compiler is picking one overload, while the VB.NET compiler is picking a different overload, in the exact same situation. Am I wrong?Regarding your third question:
Why is the VB compiler treating the input to ByRef params different for properties that are ReadOnly vs not-ReadOnly, but have an out-of-scope setter in VB
In CLR, it is not possible to pass a property as a ByRef. The VB.NET team, however, decided that this would be useful and implemented a workaround. Internally,
Me.SomeFunction(sc.SomeProperty)
is converted into either
Dim vbTemp = sc.SomeProperty
Me.SomeFunction(vbTemp)
which is called Don’t Copy Back ByRef and used, e.g., for Read-Only properies, or
Dim vbTemp = sc.SomeProperty
Me.SomeFunction(vbTemp)
sc.SomeProperty = vbTemp
which is called Copy Back ByRef and used for properties containing both a getter and setter. All these things (and a few more complicated cases) are explained in jaredpar's WebLog: The many cases of ByRef.
Now, you are saying that when the setter is out of scope, a Don’t Copy Back ByRef should be performed. However, that would lead to inconsistent behavior: SomeFunction(sc.SomeProperty)
would update sc.SomeProperty
when called inside of SomeClass
, but the same line of code would silently not update the property when called outside (because the setter is out of scope).
About your first and second question:
Why was the ByRef templated overload version of the function chosen over the ByVal Object overload version of the function? My guess is that the compiler and IntelliSense simply favor the templated versions over non-templated versions.
They favor the templated versions over a version that would require a widening cast to Object
. Overload resolution is described in detail in Chapter 11.8.1 of the Visual Basic Language Specification.
Why is Intellisense claiming one thing while the VB compiler is clearly trying something else?
Looks like a bug to me.
If is entirely possible that they pick different versions - overload resolution is a complex area that is defined in great detail in spec documents. For example, you need to specify in what order to consider use of generics, use of implicit cases / conversions / boxing, etc.
Also - remember that in C# ref
must be explicit, so the ref
version isn't even a valid overload from the code. In VB it is implicit, and (via the generic version) can use an exact match without any conversions / casts / etc.
Confusingly, it can be that overload resolution comes before accessibility checks, so the private
thing (by itself) isn't surprising.
I also don't think intellisense necessarily defines the version actually being invoked - just the overloads that are available.
精彩评论