vblang
vblang copied to clipboard
[Proposal] Customized Auto Properties
I introduced many suggestions to shorten properties syntax, as they are the largest VB blocks. Auto properties are grate, but there are many cases where they can't be used. Here is one of them: I had to write the full property body, just to call a sub in the Set section:
Dim _account As Account
Public Property Account As Account
Get
Return _account
End Get
Set(value As Account)
_account = value
Refresh()
End Set
End Property
I suggest to allow to append some code to auto implemented properties, as this:
Public Property Account As Account
On Set
Refresh()
End Set
which will be lowered to the first example. and in case of one statement set, it can be reduced to:
Public Property Account As Account On Set: Refresh()
The same can be done with On Get if needed. There is no need to use both together, as in this it will be logical to use the normal full property body, and I had a previous proposal to make it shorter for
a single-line Get or Set.
Would you be able to control if the assignment to the backing field comes before or after the On Set? It seems there may be reasons or opinions to put it either way, like validation code (before the assignment) or raising some event (after, like your refresh example above).
@gilfusion We always have the normal full body to solve any unusual case.
Alternative suggestion: Suppose we have this property:
Dim _account As Account
Public Property Account As Account
Get
Return _account
End Get
Set(value As Account)
_account = value
lblAccountName.Content = _account.DisplayName
Refresh()
End Set
End Property
I suggest to write it as half auto:
Public Property Account As Account
Set(value As Account)
_account = value
lblAccountName.Content = _account.DisplayName
Refresh()
End Set
End Property
Sense there is no WriteOnly keyword, the Get accessory can be considered auto.
And even more compact
Public Property.Set Account As Account
_account = value
lblAccountName.Content = _account.DisplayName
Refresh()
End Property.Set
And we can take out the _account = value
Public Property.Set Account As Account
lblAccountName.Content = value.DisplayName
Refresh()
End Property.Set
so, the 11-lines property becomes only 4 lines!
... so, the 11-lines property becomes only 4 lines!
Wow!, that's like 2 lines better than current VB.NET! :)
Public Property Account As Account
Get
Return _account : End Get
Set
_account = Value : lblAccountName.Content = _account.DisplayName : Refresh() : End Set
End Property
My friend, you are too concerned about the "code golf"! (https://www.barrymichaeldoyle.com/code-golf/, https://medium.com/better-programming/have-you-heard-of-code-golf-f410c8692dbc, https://softwareengineering.stackexchange.com/questions/43151/should-you-sacrifice-code-readability-with-how-efficient-code-is)
What you're aiming for is some sort of automatic backing (e.g. _account), but with "events". However, we need to be careful to not introduce any "magic code" anti-patterns. I.e. it needs to be clear how the backing is done, how the validations are done, and how any side-effects may happen.
Changing the backing is the easy part. The hard part is in the validation and side-effects - things get quite messy. In the end, I don't like it enough to be worth the trouble. But in any case, I'll put my version of this proposal in the next post.
Another way to do this (which I don't necessarily like any better)...
BACKING FIELD
First the backing. For a variable already declared, indicate that with the With clause:
Dim _account As Account
Public Property Account As Account With _account
If the variable is not declared, indicate that with the Using clause:
Public Property Account As Account Using _account
The With versus Using signals intent, to avoid accidental field usage. Using declares a private variable as explicitly named. Note that a single-line Property can still be done with With and Using.
VALIDATION
Next is the validation. That could be indicated with an optional When clause. If its condition is True, the value assignment (Set) or return (Get) may then be done. For Set, the assignment is the implied first statement; and for Get, the return is the implied last statement.
Here's where the awkwardness starts: What to do if the When condition is False? We can't allow for a silent fail; maybe some sort of Exception needs to be thrown (like InvalidOperationException)? Perhaps the When could have an optional Else - but then what? It still seems like an Exception must still happen.
Anyway, here's what your example could look like:
Public Property Account As Xyz.Account Using _account
Get
' There is no When clause, so Get body is always executed.
' The implied final statement is Return _account; it can be
' avoided by earlier Return or Exit Property statement.
End Get
Set(value As Account) When value.Balance > 0
' Set body is executed only if When condition is True.
' The implied first statement is _account = value;
' which of course does not happen if When condition is False.
lblAccountName.Content = _account.DisplayName
Refresh()
End Set
End Property
In the context of With or Using, the Get and Set blocks can now be optional. However, if validation is needed via When, then the block is needed. Since there is no When on the above Get, it can be removed:
Public Property Account As Xyz.Account Using _account
Set(value As Account) When value.Balance > 0
lblAccountName.Content = _account.DisplayName
Refresh()
End Set
End Property
Also, since VB already allows for the Set parameter to be automatic, are golfing score can now be reduced by 18 characters:
Public Property Account As Xyz.Account Using _account
Set When value.Balance > 0
lblAccountName.Content = _account.DisplayName
Refresh()
End Set
End Property
RECAP
(1) With and Using clause to indicate backing. When used, Property can be a single line, working similarly to how it now works with automatic backing.
(2a) Optional When expression for validation. Assignment/Return and Block is executed if True; otherwise optional Else statement is executed and then exception is thrown.
(2b) Missing When works the same as When True.
(3a) In Set, assignment is implied first statement of block.
(3b) In Get, return is implied last statement of block.
(4a) If Set not given, implied block of assignment.
(4b) If Get not given, implied block of return.
(5) If there is neither a Set or Get block, then Property must be a single-line (as it is today).
Sense there is no
WriteOnlykeyword
??
Yes, there is:
Sub Main
Boo = "Hello, World!"
End Sub
Writeonly Property Boo As String
Set
Console.WriteLine(value)
End Set
End Property
too concerned about the "code golf"
I hadn't seen the term before, but it explains a lot, and I do believe it to be a curse anywhere other than in an obfuscated code competition.
I have seen comments from people (not in this forum) who believe that C# is faster than VB because there are fewer characters.
I think some believe that all code is actually interpreted at run time, and don't understand how a compiler works.
I actually think there is some merit in what this proposal aims to achieve, but I think a more complete package, like @AnthonyDGreen was proposing in #282, or his earlier #194
@pricerc, Yep I agree that something like #282 is the better approach.
@rskar-git
Public Property Account As Account
Get
Return _account : End Get
Set
_account = Value : lblAccountName.Content = _account.DisplayName : Refresh() : End Set
End Property
even better:
Public Property Account As Account : Get : Return _account : End Get : Set : _account = Value : lblAccountName.Content = _account.DisplayName : Refresh() : End Set : End Property
@pricerc
Sense there is no WriteOnly keyword
in the code sample. missing WriteOnly is an indication to the language that this is a customized auto property.
in the code sample. missing
WriteOnlyis an indication to the language that this is a customized auto property.
That makes a bit more sense.
Still, I think this is horrible:
Public Property.Set Account As Account lblAccountName.Content = value.DisplayName Refresh() End Property.Set
It's almost like a VB6 "Property Set", only less clear.
In one of the articles that @rskar-git provided, there is this quote attributed to Wes Dyer (of the C# development team) :
make it correct, make it clear, make it concise, make it fast.
In that order.
In my view, this proposal would reduce clarity in an effort to make the code more concise. That is by definition, a bad thing.
I think #194 and two new interfaces (one including all properties unless excluded by an attribute, one only including properties with an attribute) and a method that implemented the interface would be fine, or even better a method with the right signature and a (new syntax) handles clause (* or explicit list).
Public Class Notify
Public Property Here as String
Public Property There as Integer
Public Sub OnCompilerMagic(Of T)(propertyName As String,
ByRef backingField As T,
ByRef value As T) Handles Here, There
If backingField
End Sub
End Class