-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Way to express "Simple" Expression Bodied Members #61
Comments
I find these types of expressions to be far less readable than a normal method/property. I realise that it's subjective though. As for less typing, the IDE already does most of the work for you. You just have to type |
I'm hesitant to add new syntax to support the scenario of property stubs for properties you just haven't implemented yet. Can you provide more examples? |
@AnthonyDGreen Is this not just a VB equivalent of C# 7's expression bodied members and throw expressions? I think the original code shown (throwing |
Public MustInherit Class Base
MustOverride ReadOnly Property Value As Int32
Overridable ReadOnly Property Text As String = "Base"
End Class
Public Class F : Inherits Base
Public Overrides ReadOnly Property Value As Integer
Get
Throw New NotImplementedException()
End Get
End Property
End Class If we could express that like auto-implemented properties. Public Class F : Inherits Base
Public Overrides ReadOnly Property Value As Integer => Throw New NotImplementedException()
End Class If we override the Public Class F : Inherits Base
Public Overrides ReadOnly Property Text As String => "First"
Public Overrides ReadOnly Property Value As Integer => Throw New NotImplementedException()
End Class 12 Loc down to 4 Loc is a big reduction of syntax ceremony, and it just a simple to read. IMHO |
I've refactored SyntaxNode as an real-world example utilising this feature. ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Runtime.CompilerServices
Imports Microsoft.CodeAnalysis.Syntax.InternalSyntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
<DebuggerDisplay("{GetDebuggerDisplay(), nq}")>
Partial Friend Class VisualBasicSyntaxNode
Inherits GreenNode
Friend ReadOnly Property Kind As SyntaxKind => Return CType(Me.RawKind, SyntaxKind)
Friend ReadOnly Property ContextualKind As SyntaxKind => Return CType(Me.RawContextualKind, SyntaxKind)
Public Overrides ReadOnly Property KindText As String => Return Me.Kind.ToString()
Public Overrides ReadOnly Property Language As String => Return LanguageNames.VisualBasic
''' <summary>Should only be called during construction.</summary>
''' <remarks>This should probably be an extra constructor parameter, but we don't need more constructor overloads.</remarks>
Protected Sub SetFactoryContext(context As ISyntaxFactoryContext)
If context.IsWithinAsyncMethodOrLambda Then SetFlags(NodeFlags.FactoryContextIsInAsync)
If context.IsWithinIteratorContext Then SetFlags(NodeFlags.FactoryContextIsInIterator)
End Sub
Friend Function MatchesFactoryContext(context As ISyntaxFactoryContext) As Boolean
Return context.IsWithinAsyncMethodOrLambda = Me.ParsedInAsync AndAlso
context.IsWithinIteratorContext = Me.ParsedInIterator
End Function
#Region "Serialization"
Friend Sub New(reader As ObjectReader)
MyBase.New(reader)
End Sub
#End Region
' The rest of this class is just a convenient place to put some helper functions that are shared by the
' various subclasses.
Public Overrides ReadOnly Property IsStructuredTrivia As Boolean => Return TypeOf Me Is StructuredTriviaSyntax
Public Overrides ReadOnly Property IsDirective As Boolean => Return TypeOf Me Is DirectiveTriviaSyntax
Public Overrides ReadOnly Property IsSkippedTokensTrivia As Boolean => Return Me.Kind = SyntaxKind.SkippedTokensTrivia
Public Overrides ReadOnly Property IsDocumentationCommentTrivia As Boolean => Return Me.Kind = SyntaxKind.DocumentationCommentTrivia
Protected Overrides Function GetSlotCount() As Integer
Throw ExceptionUtilities.Unreachable
End Function
Protected Property _slotCount As Integer
Get
Return Me.SlotCount
End Get
Set(value As Integer)
Me.SlotCount = value
End Set
End Property
Friend Function GetFirstToken() As SyntaxToken
Return DirectCast(Me.GetFirstTerminal(), SyntaxToken)
End Function
Friend Function GetLastToken() As SyntaxToken
Return DirectCast(Me.GetLastTerminal(), SyntaxToken)
End Function
' Get the leading trivia a green array, recursively to first token.
Friend Overridable Function GetLeadingTrivia() As GreenNode
Dim possibleFirstChild = GetFirstToken()
If possibleFirstChild Is Nothing Then Return Nothing
Return possibleFirstChild.GetLeadingTrivia()
End Function
Public Overrides Function GetLeadingTriviaCore() As GreenNode
Return Me.GetLeadingTrivia()
End Function
' Get the trailing trivia a green array, recursively to first token.
Friend Overridable Function GetTrailingTrivia() As GreenNode
Dim possibleLastChild = GetLastToken()
If possibleLastChild Is Nothing Then Return Nothing
Return possibleLastChild.GetTrailingTrivia()
End Function
Public Overrides Function GetTrailingTriviaCore() As GreenNode
Return Me.GetTrailingTrivia()
End Function
Protected Sub New(kind As SyntaxKind)
MyBase.New(CType(kind, UInt16))
GreenStats.NoteGreen(Me)
End Sub
Protected Sub New(kind As SyntaxKind, width As Integer)
MyBase.New(CType(kind, UInt16), width)
GreenStats.NoteGreen(Me)
End Sub
Protected Sub New(kind As SyntaxKind, errors As DiagnosticInfo())
MyBase.New(CType(kind, UInt16), errors)
GreenStats.NoteGreen(Me)
End Sub
Protected Sub New(kind As SyntaxKind, errors As DiagnosticInfo(), width As Integer)
MyBase.New(CType(kind, UInt16), errors, width)
GreenStats.NoteGreen(Me)
End Sub
Friend Sub New(kind As SyntaxKind, diagnostics As DiagnosticInfo(), annotations As SyntaxAnnotation())
MyBase.New(CType(kind, UInt16), diagnostics, annotations)
GreenStats.NoteGreen(Me)
End Sub
Friend Sub New(kind As SyntaxKind, diagnostics As DiagnosticInfo(), annotations As SyntaxAnnotation(), fullWidth As Integer)
MyBase.New(CType(kind, UInt16), diagnostics, annotations, fullWidth)
GreenStats.NoteGreen(Me)
End Sub
''' <summary>
''' Get all syntax errors associated with this node, or any child nodes, grand-child nodes, etc. The errors
''' are not in order.
''' </summary>
Friend Overridable Function GetSyntaxErrors() As IList(Of DiagnosticInfo)
If Not ContainsDiagnostics Then Return Nothing
Dim accumulatedErrors As New List(Of DiagnosticInfo)
AddSyntaxErrors(accumulatedErrors)
Return accumulatedErrors
End Function
Friend Overridable Sub AddSyntaxErrors(accumulatedErrors As List(Of DiagnosticInfo))
If Me.GetDiagnostics IsNot Nothing Then
accumulatedErrors.AddRange(Me.GetDiagnostics)
End If
Dim cnt = SlotCount()
If cnt = 0 Then Return
For i As Integer = 0 To cnt - 1
Dim child = GetSlot(i)
If child IsNot Nothing AndAlso child.ContainsDiagnostics Then
DirectCast(child, VisualBasicSyntaxNode).AddSyntaxErrors(accumulatedErrors)
End If
Next
End Sub
Private Function GetDebuggerDisplay() As String
Dim text = ToFullString()
If text.Length > 400 Then text = text.Substring(0, 400)
Return Kind.ToString & ":" & text
End Function
Friend Overloads Shared Function IsEquivalentTo(left As VisualBasicSyntaxNode, right As VisualBasicSyntaxNode) As Boolean
If left Is right Then Return True
If left Is Nothing OrElse right Is Nothing Then Return False
Return left.IsEquivalentTo(right)
End Function
' Use conditional weak table so we always return same identity for structured trivia
Private Shared ReadOnly s_structuresTable As New ConditionalWeakTable(Of SyntaxNode, Dictionary(Of Microsoft.CodeAnalysis.SyntaxTrivia, SyntaxNode))
Public Overrides Function GetStructure(trivia As Microsoft.CodeAnalysis.SyntaxTrivia) As SyntaxNode
If Not trivia.HasStructure Then Return Nothing
Dim parent = trivia.Token.Parent
If parent Is Nothing Then Return VisualBasic.Syntax.StructuredTriviaSyntax.Create(trivia)
Dim [structure] As SyntaxNode = Nothing
Dim structsInParent = s_structuresTable.GetOrCreateValue(parent)
SyncLock structsInParent
If Not structsInParent.TryGetValue(trivia, [structure]) Then
[structure] = VisualBasic.Syntax.StructuredTriviaSyntax.Create(trivia)
structsInParent.Add(trivia, [structure])
End If
End SyncLock
Return [structure]
End Function
Public Overrides Function CreateSeparator(Of TNode As SyntaxNode)(element As SyntaxNode) As CodeAnalysis.SyntaxToken
Dim separatorKind As SyntaxKind = SyntaxKind.CommaToken
If element.Kind = SyntaxKind.JoinCondition Then
separatorKind = SyntaxKind.AndKeyword
End If
Return VisualBasic.SyntaxFactory.Token(separatorKind)
End Function
Public Overrides Function IsTriviaWithEndOfLine() As Boolean
Return Me.Kind = SyntaxKind.EndOfLineTrivia OrElse Me.Kind = SyntaxKind.CommentTrivia
End Function
End Class
End Namespace |
And SyntaxLiterals ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
Friend Partial Class CharacterLiteralTokenSyntax
Friend NotOverridable Overrides ReadOnly Property ObjectValue As Object => Return Me.Value
End Class
Friend Partial Class DateLiteralTokenSyntax
Friend NotOverridable Overrides ReadOnly Property ObjectValue As Object => Return Me.Value
End Class
Friend Partial Class DecimalLiteralTokenSyntax
Friend NotOverridable Overrides ReadOnly Property ObjectValue As Object => Return Me.Value
End Class
Friend Partial Class StringLiteralTokenSyntax
Friend NotOverridable Overrides ReadOnly Property ObjectValue As Object => Return Me.Value
Friend NotOverridable Overrides ReadOnly Property ValueText As String => Return Me.Value
End Class
Friend NotInheritable Class IntegerLiteralTokenSyntax(Of T)
Inherits IntegerLiteralTokenSyntax
Friend ReadOnly _value As T
Friend Sub New(kind As SyntaxKind, text As String, leadingTrivia As GreenNode, trailingTrivia As GreenNode, base As LiteralBase, typeSuffix As TypeCharacter, value As T)
MyBase.New(kind, text, leadingTrivia, trailingTrivia, base, typeSuffix)
Me._value = value
End Sub
Friend Sub New(kind As SyntaxKind, errors As DiagnosticInfo(), annotations As SyntaxAnnotation(), text As String, leadingTrivia As GreenNode, trailingTrivia As GreenNode, base As LiteralBase, typeSuffix As TypeCharacter, value As T)
MyBase.New(kind, errors, annotations, text, leadingTrivia, trailingTrivia, base, typeSuffix)
Me._value = value
End Sub
Friend Sub New(reader As ObjectReader)
MyBase.New(reader)
Me._value = CType(reader.ReadValue(), T)
End Sub
Shared Sub New()
ObjectBinder.RegisterTypeReader(GetType(IntegerLiteralTokenSyntax(Of T)), Function(r) New IntegerLiteralTokenSyntax(Of T)(r))
End Sub
Friend Overrides Sub WriteTo(writer As ObjectWriter)
MyBase.WriteTo(writer)
writer.WriteValue(Me._value)
End Sub
''' <summary>The value of the token.</summary>
Friend ReadOnly Property Value As T => Return Me._value
Friend Overrides ReadOnly Property ValueText As String => Return _value.ToString
Friend Overrides ReadOnly Property ObjectValue As Object => Return Me.Value
Public Overrides Function WithLeadingTrivia(trivia As GreenNode) As GreenNode
Return New IntegerLiteralTokenSyntax(Of T)(Kind, GetDiagnostics, GetAnnotations, Text, trivia, GetTrailingTrivia, _base, _typeSuffix, _value)
End Function
Public Overrides Function WithTrailingTrivia(trivia As GreenNode) As GreenNode
Return New IntegerLiteralTokenSyntax(Of T)(Kind, GetDiagnostics, GetAnnotations, Text, GetLeadingTrivia, trivia, _base, _typeSuffix, _value)
End Function
Friend Overrides Function SetDiagnostics(newErrors As DiagnosticInfo()) As GreenNode
Return New IntegerLiteralTokenSyntax(Of T)(Kind, newErrors, GetAnnotations, Text, GetLeadingTrivia, GetTrailingTrivia, _base, _typeSuffix, _value)
End Function
Friend Overrides Function SetAnnotations(annotations As SyntaxAnnotation()) As GreenNode
Return New IntegerLiteralTokenSyntax(Of T)(Kind, GetDiagnostics, annotations, Text, GetLeadingTrivia, GetTrailingTrivia, _base, _typeSuffix, _value)
End Function
End Class
''' <summary>
''' Represents an integer literal token.
''' </summary>
Friend MustInherit Class IntegerLiteralTokenSyntax
Inherits SyntaxToken
Friend ReadOnly _base As LiteralBase
Friend ReadOnly _typeSuffix As TypeCharacter
Friend Sub New(kind As SyntaxKind, text As String, leadingTrivia As GreenNode, trailingTrivia As GreenNode, base As LiteralBase, typeSuffix As TypeCharacter)
MyBase.New(kind, text, leadingTrivia, trailingTrivia)
Me._base = base
Me._typeSuffix = typeSuffix
End Sub
Friend Sub New(kind As SyntaxKind, errors As DiagnosticInfo(), annotations As SyntaxAnnotation(), text As String, leadingTrivia As GreenNode, trailingTrivia As GreenNode, base As LiteralBase, typeSuffix As TypeCharacter)
MyBase.New(kind, errors, annotations, text, leadingTrivia, trailingTrivia)
Me._base = base
Me._typeSuffix = typeSuffix
End Sub
Friend Sub New(reader As ObjectReader)
MyBase.New(reader)
Me._base = CType(reader.ReadByte(), LiteralBase)
Me._typeSuffix = CType(reader.ReadByte(), TypeCharacter)
End Sub
Friend Overrides Sub WriteTo(writer As ObjectWriter)
MyBase.WriteTo(writer)
writer.WriteByte(CType(Me._base, Byte))
writer.WriteByte(CType(Me._typeSuffix, Byte))
End Sub
''' <summary>Whether the token was specified in base 10, 16, 8, or 2.</summary>
Friend ReadOnly Property Base As LiteralBase => Return Me._base
''' <summary>
''' The type suffix or type character that was on the literal, if any. If no suffix
''' was present, TypeCharacter.None is returned.
''' </summary>
Friend ReadOnly Property TypeSuffix As TypeCharacter => Return Me._typeSuffix
End Class
''' <summary>
''' Represents an floating literal token.
''' </summary>
Friend NotInheritable Class FloatingLiteralTokenSyntax(Of T)
Inherits FloatingLiteralTokenSyntax
Friend ReadOnly _value As T
Friend Sub New(kind As SyntaxKind, text As String, leadingTrivia As GreenNode, trailingTrivia As GreenNode, typeSuffix As TypeCharacter, value As T)
MyBase.New(kind, text, leadingTrivia, trailingTrivia, typeSuffix)
Me._value = value
End Sub
Friend Sub New(kind As SyntaxKind, errors As DiagnosticInfo(), annotations As SyntaxAnnotation(), text As String, leadingTrivia As GreenNode, trailingTrivia As GreenNode, typeSuffix As TypeCharacter, value As T)
MyBase.New(kind, errors, annotations, text, leadingTrivia, trailingTrivia, typeSuffix)
Me._value = value
End Sub
Friend Sub New(reader As ObjectReader)
MyBase.New(reader)
Me._value = CType(reader.ReadValue(), T)
End Sub
Shared Sub New()
ObjectBinder.RegisterTypeReader(GetType(FloatingLiteralTokenSyntax(Of T)), Function(r) New FloatingLiteralTokenSyntax(Of T)(r))
End Sub
Friend Overrides Sub WriteTo(writer As ObjectWriter)
MyBase.WriteTo(writer)
writer.WriteValue(Me._value)
End Sub
''' <summary>The value of the token.</summary>
Friend ReadOnly Property Value As T => Return Me._value
Friend Overrides ReadOnly Property ValueText As String => Return _value.ToString
Friend Overrides ReadOnly Property ObjectValue As Object => Return Me.Value
Public Overrides Function WithLeadingTrivia(trivia As GreenNode) As GreenNode
Return New FloatingLiteralTokenSyntax(Of T)(Kind, GetDiagnostics, GetAnnotations, Text, trivia, GetTrailingTrivia, _typeSuffix, _value)
End Function
Public Overrides Function WithTrailingTrivia(trivia As GreenNode) As GreenNode
Return New FloatingLiteralTokenSyntax(Of T)(Kind, GetDiagnostics, GetAnnotations, Text, GetLeadingTrivia, trivia, _typeSuffix, _value)
End Function
Friend Overrides Function SetDiagnostics(newErrors As DiagnosticInfo()) As GreenNode
Return New FloatingLiteralTokenSyntax(Of T)(Kind, newErrors, GetAnnotations, Text, GetLeadingTrivia, GetTrailingTrivia, _typeSuffix, _value)
End Function
Friend Overrides Function SetAnnotations(annotations As SyntaxAnnotation()) As GreenNode
Return New FloatingLiteralTokenSyntax(Of T)(Kind, GetDiagnostics, annotations, Text, GetLeadingTrivia, GetTrailingTrivia, _typeSuffix, _value)
End Function
End Class
''' <summary>Represents an floating literal token.</summary>
Friend MustInherit Class FloatingLiteralTokenSyntax
Inherits SyntaxToken
Friend ReadOnly _typeSuffix As TypeCharacter
Friend Sub New(kind As SyntaxKind, text As String, leadingTrivia As GreenNode, trailingTrivia As GreenNode, typeSuffix As TypeCharacter)
MyBase.New(kind, text, leadingTrivia, trailingTrivia)
Me._typeSuffix = typeSuffix
End Sub
Friend Sub New(kind As SyntaxKind, errors As DiagnosticInfo(), annotations As SyntaxAnnotation(), text As String, leadingTrivia As GreenNode, trailingTrivia As GreenNode, typeSuffix As TypeCharacter)
MyBase.New(kind, errors, annotations, text, leadingTrivia, trailingTrivia)
Me._typeSuffix = typeSuffix
End Sub
Friend Sub New(reader As ObjectReader)
MyBase.New(reader)
Me._typeSuffix = CType(reader.ReadByte(), TypeCharacter)
End Sub
Friend Overrides Sub WriteTo(writer As ObjectWriter)
MyBase.WriteTo(writer)
writer.WriteByte(CType(Me._typeSuffix, Byte))
End Sub
''' <summary>
''' The type suffix or type character that was on the literal, if any. If no suffix
''' was present, TypeCharacter.None is returned.
''' </summary>
Friend ReadOnly Property TypeSuffix As TypeCharacter => Return Me._typeSuffix
End Class
End Namespace |
Maybe it's because I'm already familiar with this from C#, but I see the two refactored examples @AdamSpeight2008 did as being more readable/understandable/concise. Particularly in the case of |
@AdamSpeight2008 Using vb If not implemented, then we can write this simple expression: Public ReadOnly Property Example As Foo With New NotImplementedException |
@xieguigang |
I'm OK with
It's a subtle point, but So, instead of
|
@rskar-git Would the compiler complain ? Friend Overrides ReadOnly Property CanConstruct As Boolean Get Throw New NotSupportedException()
Friend ReadOnly Property Kind As SyntaxKind Get Return CType(Me.RawKind, SyntaxKind) about incomplete Friend Overrides ReadOnly Property CanConstruct As Boolean
Get
Throw New NotSupportedException()
End Get
End Property
Friend ReadOnly Property Kind As SyntaxKind
Get
Return CType(Me.RawKind, SyntaxKind)
End Get
End Property |
(I hope I'm not misunderstanding your question.) I was merely suggesting The But maybe I'm being naive. It just seems to me that As I said before, I'm OK with |
@rskar-git |
It's clear now I need to really get familiar with how the VB compiler actually works; I'm (not yet) at any place to say whether or not a reparse would be likely. But you've got me wondering ... is that what happens now for |
Would it possible to support writing as well, i think it would allow simpler implementations of various patterns. Public ReadOnly Property Value As String => m_object.Value
Public Property Value As String => m_object.Value
Public ReadOnly Property Value As String => Throw New Exception()
Public Property Value As String => Throw New Exception()
Public Overrides ReadOnly Property Value As Integer => 2
Public Overrides Property Value As Integer => 2 ' Error
Private m_LastName As String = ""
Public Property FirstName As String => Me.Value("FirstName")
Public Property LastName As String => Me.Value(m_LastName)
Public Property Email As String => Me.Value(NameOf(Me.Email))
Public Property BirthDate As Date => Me.Value() Public Enum Data
Address
Email
Phone
End Enum
Class Customer
Inherits Model
Private m_Id As Integer
Private m_FirstName As Integer
Property Id As Integer => MyBase.Value(m_Id)
Property FirstName As String => MyBase.SetOrGet(m_FirstName)
Property LastName As String => MyBase.Value
Property BirthDate As Date => MyBase.Value
Property Address As String => MyBase.Value(Data.Addres)
Property Email As String => MyBase.Value(Data.Email)
Property Phone As String => MyBase.Value(Data.Phone)
End Class
Class Model
Public Property Value(Of T)(<CallerMemberName> propertyName As String) As T
...
End Property
Public Property Value(Of T)(data As Data) As T
...
End Property
Private Property Value(Of T)(ByRef field As T, ByVal value) As T
...
End Function
'Or something like this since ByRef is not supported in properties
Private Sub SetOrGet(Of T)(ByRef field As T, value As T, isGet As Boolean ) As T
End Sub
End Class |
@edin I've not looked into supporting Public Property Value As T
Get
Return _Value
End Get
' Eg the 'newValue parameter of the setter
Set( newValue As T )
_Value = newValue
End Set
End Property Some languages reserve the name If you found a way, please don't be a scared to post your idea. As it may be the one we need. |
@AdamSpeight2008 Actually, VB already supports that. The argument list for a set accessor is optional. If it's left off,
And yes, it still works if the property is called |
Here is more details how could this works: Following syntax Public Property Name As String => {Expression} would be expanded to get and optinaly set depending if it's possible to construct assigment for given expression. When ReadOnly is specified then setter will not be generated. In case that it's not possible to construct assigment to the expression e.g. constant value, then property needs to be ReadOnly. (Instead of => maybe it could be used just = sign.) Public Property Name As String => {Expression}
Get
Return {Expression}
End Get
Set(value As String)
{Expression} = value
End Set
End Property
' In case of throw expression
Public Property Name As String => {ThrowExpression}
Get
{ThrowExpression}
End Get
Set(value As String)
{ThrowExpression}
End Set
End Property Here are some more examples how it could be used.
Class Customer
Public Property ID As Integer
Public Property Name As String
End Class
Class Animal
Public Property ID As Integer
Public Property Name As String
End Class
Class CustomerView
Private m_Customer As Customer
Private m_Animal As Animal
Public ReadOnly Property CustomerID As Integer => m_Customer.ID
Public ReadOnly Property AnimalID As Integer => m_Animal.ID
Public Property CustomerName As String => m_Customer.Name
Public Property AnimalName As String => m_Animal.Name
End Class An extension to this would be to add support for chaining properties, but generic indexed properties and byref parameters are not currently supported so i guess it would be lot's of work.
Class Customer
Private m_ID As Integer
Private m_FirstName As String
Private m_LastName As String
Public ReadOnly Property Id As Integer => CustomIndexer(m_ID)
Public Property FirstName As String => CustomIndexer(m_FirstName)
Public Property LastName As String => CustomIndexer(m_LastName)
' More properties
Public Property IsChanged As Boolean
Private Property CustomIndexer(Of T) (ByRef field As T) As T
Get
return field
End Get
Set(value As T)
field = value
IsChanged = True
End Set
End Property
End Class
Class Customer
Public Property ID As Integer
Public Property FirstName As String
Public Property LastName As String
...
End Class
Class CustomerProxy
Private m_Customer As Customer
Public Property FirstName As String => PropIndexer(m_Customer.FirstName)
Public Property LastName As String => PropIndexer(m_Customer.LastName)
Private Property PropIndexer(Of T) (prop As PropertyExpression) As T
Get
Return CType(prop.GetValue() , T)
End Get
Set(value As T)
prop.SetValue(value)
End Set
End Property
End Class
Class Customer
Private m_Map As New Dictionary(Of String, Object)
Public Property Id As Id => CustomIndexer()
Public Property FirstName As String => CustomIndexer()
Public Property LastName As String => CustomIndexer()
Private Property CustomIndexer(Of T) (<CallerMemberName> name As String = Nothing) As T
Get
Return CType(m_Map(name), T)
End Get
Set
m_Map(name) = value
End Set
End Property
End Class
Class Customer
Private m_DataRow As DataRow
Public Property Id As Id => DataRow("col_ID")
Public Property FirstName As String => DataRow("col_FirstName")
Public Property LastName As String => DataRow("col_LastName")
Private Property DataRow(Of T) (name as String) As T
Get
Return CType(m_DataRow(name), T)
End Get
Set
m_DataRow(name) = value
End Set
End Property
End Class |
Highly recommend using
Possible syntax options
I cannot say enough how much this feature would mean to me, in expressing 'computed properties' / expressions concisely in data classes -- makes a big difference as far as being able to split them up and compose them together. Hoping we can get a timeline on this from the VB team. (In general features that are already in C# are a good candidate for inclusion in VB, barring reasons specifically not to, correct? And it's just a question of resources?) |
I have a feeling that So, if I'm understanding things right, then:
What if hooks to events or interfaces necessary? Perhaps do
Add to that as many adaptations of C# usage of Anyway, below is hypothetical VB syntax to consider.
|
I just thought of this, the following:
Could be done like this (idea from @edin):
|
How about a keyword like
|
|
Regarding mapped properties (Get/Set in one line)I see that a lot of people (@edin, @rskar-git) have expressed interest in doing this for Property Get/Set in one line -- i.e. While I appreciate the utility of the idea for concisely defining what could be called a "mapping," I must say that
Either way, it is critical that this not be the only way to do expression-bodied properties; we have to allow for Get/Set separately as well, and also for |
@bandleader @AdamSpeight2008 gave a thumbs-up to the post @edlin made on Apr 22, 2017, which elaborates on this "mapped properties" concept. All I did was paraphrase that, and then attempted to make some other hypothetical syntax (using C# examples as a go-by). I'm just trying to follow along. In the meantime, I believe AdamSpeight2008 is working on the prototype, and I don't know if he's looking into "mapped properties" or not. You may be right that something like:
may be something rather complex to implement, but wow! it's so handy it would certainly be worth trying. |
@rskar-git I'm not currently working a prototype for this, wanting try and finish up some of the others first. |
OK, so in summary for the VB team to review: (@KathleenDollard et al) '=== Subs/Functions:
Function AddTwo(i As Integer) As Integer => i + 2
Sub PrintInt(i As Integer) => Debug.Print(i)
'=== Property get/set:
Property MyProp(i As Integer) As Integer
Get => i + 2
Set => Debug.Print("Done; thank you!")
End Property
'=== Read-only property -- only one line necessary:
ReadOnly Property MyProp(i As Integer) As Integer => i + 2 Things to be done later if at all
Comments/feedback welcome from both community and VB team. |
VB has many strengths vs the C-esque languages, a key one is that it doesn't aim for terse for the sake of being terse. I like this because terseness doesn't help the maintainer of the code who comes along in two years time and has to de-terse the code before they can debug it. As part of that, I don't see any particular need to seek 'feature parity' with C# for its own sake, especially when it's for things that don't radically improve the language. For example, I've never been a fan of anonymous methods, because when I'm writing code, I'm always thinking about the fact that I'm going to have to debug and tune it, and like to be able to add instrumentation code to methods, and to be able to see all my methods in a stack trace. And about 1/2 the time I've used anonymous methods, I've ended up refactoring them into actual methods anyway. So while I get the goals in this thread, adding anonymous property getters using what looks like a C# lambda construct just feels wrong. I realize that 'legibility' is subjective, but it's also contextual - what I find legible in C# code, I don't necessarily find legible in VB, and vice-versa. E.g. in this case, I don't find this: Public Overrides ReadOnly Property Value As Integer => Throw New NotImplementedException() more legible than the 'traditional' Public Overrides ReadOnly Property Value As Integer
Get
Throw New NotImplementedException()
End Get
End Property In fact, I find it quite the opposite. Possibly just because the latter is a pattern I've been looking at for the last 15 years, but I still think it presents better. And as someone has mentioned somewhere on this forum, would be easier for a non-programmer looking over a shoulder to understand. I think that declarative stuff, like the @anthonygreen's stuff in #282 is ok, because it's about reducing repetitive boiler-plate code, and I can see how it will reduce errors, whereas I see these sneaky beggars as being more likely to increase them |
@pricerc I can't see at all how you think the traditional way is more readable than the expression-bodied way. Nor how a non-programmer could understand it better.
Dim x As Integer
Set
x = 5
End Set
End Dim |
ReadOnly Property ThisTime As Date = DateTime.Now
ReadOnly Property ThatTime As Date => DateTime.Now Maybe this isn't an issue in languages where this syntax currently being used, but I can see there being some confusion between those two lines of code, which do quite different things. |
@bandleader, as I said, style preferences are subjective, a personal preference. So yes, surprising though it may be, I do prefer multi-line boiler plate for a method definition - I've been troubled by enough one-line anonymous methods whilst debugging, that I now avoid them. And yes, I have refactored more than one single-line lambda into a discrete method - usually while debugging a performance problem, and in both C# and VB. I'd also argue there is a subtle but important difference between
The former I expect to happen once as part of class instantiation, and be optimized into the class constructor, whereas the latter is effectively a method declaration. For me (and I'm sure at least some others), the multi-line declaration is a pattern I recognise when scanning for methods, which are different from assignments. My main concern is about maintenance. I don't know about you, but I sometimes have to maintain code that I haven't touched in several years, and that process often starts with scanning over the code; for me, being able to easily differentiate between methods and assignments is helpful in rebuilding an understanding of what it does. Currently, this is easy, as there is a clear distinction. But as mentioned by @gilfusion having to differentiate between these two would slow that process for me: ReadOnly Property ThisTime As Date = DateTime.Now
ReadOnly Property ThatTime As Date => DateTime.Now |
There is a suggestion in C# to use Property ThisTime As Date = DateTime.Now ' initial value
Property ThatTime As Date <=> mydate ' map mydate to set and get |
@AdamSpeight2008 @reduckted @AnthonyDGreen @scottdorman @xieguigang @KathleenDollard Property ThatTime As Date Using mydate I like this one. Using is reserved and gives a clearer meaning the the property is relying on the var or expression. Property ThatTime As Date Using mydate = Date.Now Which is equivalent to: Dim mydate = Date.Now
Property ThatTime As Date
Get()
return mydate
End get
Set (Value as Date)
mydate = Value
End Set
End Property |
This priposal is here for one yaer, and tey no decision made? |
I don't understand how this could ever be useful. If The exception would be if you had an existing public |
#403 resurrected this concept, but along the way in there, a slightly alternative form for a 'lambda-ish' readonly form spring to mind. Rather than using ' instead of
Friend ReadOnly Property Kind As SyntaxKind => Return CType(Me.RawKind, SyntaxKind)
' a more VB style
Friend ReadOnly Property Kind As SyntaxKind = Get CType(Me.RawKind, SyntaxKind)
The Parser would process |
The overall goal as I interpret it is to enjoy a convenient (and highly readable) single-line syntax similar to C#. I admit it would seem a little odd to only have access to Would there be any ambiguity or limitation with just using
|
I guess that's a subjective style choice. Either would work, but here's my thinking: #400 suggested an alternative handling of iterators (basically replacing More completely, it goes a bit like:
|
Maybe if somebody wants to use the default property in order to avoid ' *partial*-default property
Public Property Content As List(Of String)
Get With Default
Set(Value As List(Of String))
If Value Is Nothing Then Throw New NullReferenceException()
Content = Value ' Access with property name
End Set
End Property where getter equals to this: Get
Return Content ' or equivalent inner private variable
End Get We can even go further with setter(currently adds a kinda-frequently-used keyword Set(Value As List(Of String)) _
Checks Value IsNot Nothing Else Throw New NullReferenceException() _
With Default |
Auto ReadOnly Properties are convenient for when we can express our intention at initialization
Sometime I like it to just as convenient to express what should happen at invocation, especially if it just a simple express / statement (IE a single Return / Throw ).
It would reduce the amount you'd have to read and type 80% per a "simple" property.
The text was updated successfully, but these errors were encountered: