Open antarr opened 6 years ago
How do you anticipate declaring the ViewStateProperty?
@KathleenDollard There was a problem with my code above. I've updated it.
@KathleenDollard This is just a rough attempt.
Private Class ViewStatePropertyAttribute
Inherits Attribute
Public Overrides Function Getter(<CallerMemberName> Optional memberName As String = Nothing) As Object
Return ViewState(NameOf(memberName))
End Function
End Class
Ha! @antarr, you're in super luck. I literally just got a prototype of a design for something like this working this very week. I'm typing up the issue right now. I'll make sure to include this scenario.
@antarr please check this out: https://github.com/dotnet/vblang/issues/282
It mostly is what you're asking for. It's missing an important piece for your scenario which is a way to opt out of backing field generation entirely for an auto-prop. I'd like to do this but didn't include it in my initial proposal.
While I love @AnthonyDGreen's #282, I just wanted to point out that this scenario is also resolved (and actually, most elegantly resolved) using expression-bodied properties (#61) -- assuming we follow the proposal there to allow assignment as well.
Public Property sQuestionaire As Object => Me.ViewState(NameOf(sQuestionaire))
@AnthonyDGreen
I'm not yet seeing how #282 solves this. What would the syntax be?
Ah, I see. I guess I used weaker language there than I intended.
The syntax is the same. #282 proposes a way for an attribute to specify user-defined methods which are called by the compiler in either the Set
of an auto-prop, or the Get
of an auto-prop, or both. All of my examples only showed handling the Set
because that's the more common scenario (and all I had implemented at the time). But it was always the intention to support Get
handlers, specifically for scenarios like this. @antarr gave ViewState
as his use case, which is perfect. But the scenario can easily extend to things like strongly-typed wrappers around JObject
instances, ExpandoObject
property bags, WMI classes, sparse object graphs, lazily loaded properties, resource classes, etc.
I've updated my prototype to support that scenario for the sake of playing with those ideas. Here's what it looks like (this example is straight out of the unit tests):
Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Console
MustInherit Class PropertyHandlerAttribute
Inherits Attribute
End Class
Class WrapperAttribute
Inherits PropertyHandlerAttribute
End Class
Class ListingInfo
Public ReadOnly ViewState As IDictionary(Of String, Object) = New Dynamic.ExpandoObject()
Protected Sub WrapperOnPropertyGet(Of T)(propertyName As String, ByRef backingField As T, ByRef value As T)
value = ViewState(propertyName)
End Sub
Protected Function WrapperOnPropertySet(Of T)(propertyName As String, ByRef backingField As T, ByRef value As T) As Boolean
ViewState(propertyName) = value
' Never store values in the backing field.
Return True
End Function
<Wrapper>
Property Rating As Integer = 3
<Wrapper>
Property Category As String = "General"
<Wrapper>
Property Subcategory As String = "<None>"
Function BackingFieldsToString() As String
Return _Rating & _Category & _Subcategory
End Function
End Class
Module Program
Sub Main()
Dim listing = New ListingInfo
Write(listing.BackingFieldsToString())
Write(listing.Rating & listing.Category & listing.Subcategory)
Write(listing.ViewState!Rating & listing.ViewState!Category & listing.ViewState!Subcategory)
End Sub
End Module
In this example the handler is very tied to the class where the wrapper attribute is being used, but it's easy enough to break that out into a reusable base class, e.g. WrapperObject
that defines the handler methods and then each of your ViewState
wrapper types can just inherit it and tag the appropriate properties.
There's one missing ingredient from this approach, which may or may not matter to you. As it is today the auto-props will always declare a field though do to the nature of this handler they aren't used. For scenarios like this one could imagine some mechanism which tells the compiler to not bother with a backing field at all as an alternate store is being provided by the handler. I did not include that in the original issue as it's somewhat orthogonal to the core design of the property handlers feature. For some cases you might still want the backing fields for caching (e.g. <Lazy>
) while for others you very much don't want the field to save memory (e.g. <Sparse>
).
Got it. I didn't see how the backing field helped in this case.
I want to move this conversation into #282
I want to create a custom attribute that overrides the getter and setter of a property so that I can reuse some logic.
CASE
I want to create an attribute for properties that use the ViewState instead of a backing field. I can't do this without being able to override the getter and setter.
MY Code without the attribute
New Code with the attribute