Partager via


Power4: Custom property templates and custom event templates

[This post is part of a series, "wish-list for future versions of VB"]

 

IDEA: Custom property templates. We should have an easy way for users to write boiler-plate code in their properties. This would include things like INotifyPropertyChanged, logging, validation. Here's how:

Class C

    Implements INotifyPropertyChanged

    Public Event PropertyChanged(s As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged

    Property Width As Integer From New MyProperty(0, 10, PropertyChangedEvent)

    Property Height As Integer From New MyProperty(0, 5, PropertyChangedEvent)

End Class

The "Property From ..." syntax means that this property should be implemented by the specified object, in this case a new instance of a class into which we put our boilerplate:

Class MyProperty

    Private notify As PropertyChangedEventHandler

    Private min, max As Integer

    Private _p As Integer

    Sub New(min As Integer, max As Integer, notify As PropertyChangedEventHandler)

        Me.min = min : Me.max = max : Me.notify = notify

    End Sub

    Public Function [Get]() As Integer

        Return _p

    End Function

    Public Sub [Set](value As Integer)

        If _p = value Then Return

        Dim propName = Reflection.GetCurrentMethod().Name.
Replace("set_", "").Replace("get_", "")

        If value < min OrElse value > max Then Throw New ArgumentOutOfRangeException(propName)

        _p = value

        Log("property " & propName & " changed to " & value)

        notify(Me, New PropertyChangedEventArgs(propName))

    End Sub

End Class

The compiler implements a simple getter which just calls into MyProperty.Get(), and a simple setter which just calls MyProperty.Set(value). The user can decide what kind of boilerplate code to put inside their custom property class, or what parameters to pass to its constructor. The "From" expression also allows you to provide any expression as the property template, e.g. a factory method, just so long as it returns a type on which the compiler can call Get() or Set(value). These Get/Set methods may even be extension methods.

 

SCENARIO: User has a lot of properties which all behave in pretty similar ways. It's tedious to have to write the same boilerplate code over and over again. It would be better to abstract the common behavior into a single place.

 

Note: This proposal lacks a handy way to specify what attributes should be placed on the property. For instance, we have to be able to say how the property should be serialized.

 

IDEA: Property tear-offs. We could obtain an object that represents a property, and lets you call its getter or setter explicitly:

        Dim w = AddressOf x.Width

   Dim i = w.Get()

        w.Set(i)

 

IDEA: Custom event templates. The same "custom property template" idea above could be applied to "custom event templates", where there is also a lot of boiler-plate code.

 

 

These ideas are only half-baked as yet. We recognize that there is tremendous user demand for custom property templates, but we don't think we've hit upon the best solution yet. Please, write back to say whether these ideas would accomodate the kinds of properties that you want. And if you have better ideas for custom property templates, please write with them.

Unanswered questions: would custom event templates help with WPF routed events? would joins in the style of "Concurrent Basic" be expressible using custom event templates?

There's also a big question: could all of this be better handled by the IDE?  Maybe we could use IDE projection-buffers, or "snippets on steroids". 

Finally, note that the trick to obtain "propName" from MethodBase.GetCurrentMethod is a bit ugly: it prevents inlining and requires you to put it inside the property itself. One idea here I'll blog about later today, "Power6: __CALLER_MEMBER__". It might be better to always pass the property's name as a string to the getter/setter, or somehow to the constructor.

 

Provisional evaluation from VB team: There's a decent idea lurking here, worth considering against the other ideas, but only if we're confident that we can design this one right.

Comments

  • Anonymous
    February 02, 2010
    The comment has been removed

  • Anonymous
    February 05, 2010
    What about slightly different approach: Specify property template as XML (like snippet) in XML file. (If you really want to make it reusable and have it "compiled" in assembly, it can be placed there as resource. Otherwise just have such XML file as part of project) The template can contain  everything what snippet can contain - Property, Backing field, OnXxxChanged event, WPF stuff, attributes, XML doc comments etc. Compiler takes the template in compile time, does some text-based replacements there and compiles result of it (more like C++ macro). It's up to template-creator if the template will be created such way it generates 50 lines for each property or if it calls some common validation/storage etc. methods. Disadvantage might be that template is not language-independent. Advantage is that it is very flexible. ==== Templates.xml ===== <?xml version="1.0" encoding="utf-8"?> <templates xmlns="http://visual-basic.net/templates">    <template name="NotNullProperty">        <var name="$Name$" init="Name"/>        <var name="$Type$" init="Type"/>        <var name="$Field$" init='"_" + Name.ToLower'/>        <var name="$Doc$" init="XmlComment"/>        <var name="$Attrs$" init="Attributes"/>        <code>        <![CDATA[            /// <summary>Contains value of the <see cref="$Name$"/> property</summary>            Private $Field$ As $Type$            $Doc$            <exception cref="ArgumentNullException">Property is being set to <see langword="null"/></exception>            $Attrs$            Public Property $Name$ As $Type$                <DebuggerStepThrough>                Get                    Return $Field$                End Get                <DebuggerStepThrough>                Set(ByVal value As $Type$)                    If value Is Nothing Then Throw New ArgumentNullException("$Name$ cannot be null","value")                    $Field$ = value                End Set            End Property        ]]>        <code>    </template> </templates> === Class.vb  ============== Class [Class]    /// <summary>Gets or sets value</summary>    Property Value As String From NotNullProperty End Class

  • Anonymous
    February 13, 2010
    We use code gen for dependency property implementation at the moment, but something like this could potentially be a much simpler alternative. Personally, I prefer code syntax solutions to IDE features (I read a lot of code outside the VS IDE), but I’m not sure of the best syntax for this...

  • Anonymous
    March 02, 2010
    Um interesting. Might be worth talking to Rocky Lhotka as this looks similar to what you do in CSLA business objects with that frameworks Property Manager.

  • Anonymous
    March 04, 2012
    Interesting. Anyway How will you force a getter and a setter? doesn't it have to fulfill a contract by implementing an interface etc.?