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
WriteOnly
keyword
??
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
WriteOnly
is 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