Powershell: Properties with accessor and mutator methods

Created on 9 Sep 2016  路  21Comments  路  Source: PowerShell/PowerShell

Copied from TFS 3231229

We should be able to do something along these lines

class foo
{
  [string]$bar {
    get { 
      #getter code
    }
    set {
      # setter code
    }  
  }
}
Issue-Enhancement Up-for-Grabs WG-Language

Most helpful comment

I have this mostly done. Implementation needs some cleaning up, may need some design changes, and still needs tests and semantic checks, but it all works. Aiming to get a PR up this weekend or next.

Progress in this branch.

All 21 comments

@vors This is interesting. Could you clarify: this requires not only changes in the parser?

It will be at least these places:

  • Parser
  • ASTs
  • PSType.cs

@vors Thanks! I went thinking :-)

@PowerShellTeam Please assign me.

Are we sure this is the syntax we want?
How does it interact with existing syntax?
What about the following:

# readonly property
class A {
   [int]$i {get;} # ??? explicit backing property
   A($i){
      $this.i = $i
   }
}
# getter setter: do we have a private backing field?
class A {
   [int]$i {
        get {
           # what here?
        } 
        set {
            if ($value -lt 0) {throw 'x'}
            $i_ = $value #field derived from propname ?  or 'private [int] $i_'?
         }
   } = 4 # default value 
}

There's been a lot of work done i C# to handle a lot of these cases, and I think it is worth to take some time to think through what we want to achieve here. And I think there is a lot to learn from other languages here.

Let us list and discuss the scenarios before jumping to implementation.

@powercode I am full agree.
Now I am preparing the base for these enhancements. I am on parser step and completed a parser for get/set {...} (Parser error recovery takes a long time).
I can inform you that the original code already does implicit get/set and backing field, and attribute processing.
Now I plan to do as soon as possible a PR with the parser and get feedback. Then do next step and make AST and compile for get/set {...}. If this discussion will adopt new features, then I can always add them immediately or in subsequent PRs because the base will be ready.

It seems the code is ready for testing.
I remember that @lzybkr spoke about the specifics of these tests.
Where will we locate these tests? In Scripting.Classes.BasicParsing.Tests.ps1?
Any advise before I begin?
Do it makes sense to make PR now and add tests later?

Sorry, I missed commenting on this. I don't have a strong preference on tests. BasicParsing.Tests.ps1 is probably not the place for anything that tests semantics.

You can submit a PR whenever you like - but if there aren't any tests, of course you'll be asked to add them.

I have this mostly done. Implementation needs some cleaning up, may need some design changes, and still needs tests and semantic checks, but it all works. Aiming to get a PR up this weekend or next.

Progress in this branch.

@SeeminglyScience I have very old (2 year) implementation (90%) in the branch https://github.com/iSazonov/PowerShell/tree/classgetset-4
You can (must :smile:) grab tests from the branch.
Also I quickly look your implementation and think you could find useful ideas in my branch too.

@iSazonov Hmm looking at your branch now, I don't think these tests have a ton of overlap. Your design is very clever, though I was looking for a more traditional implementation.

Re design. I tried to cover all the options that users can type that is very user-friendly. I hope I achieved it.
I also tried to cover it all with tests which can save you a lot of time.

Re implementation. It was my first exercise. I did not know C# at all, do not know PowerShell insides, and haven't programmed for more than twenty years. So your implementation is in more right direction. Using a tokenizer is exactly the right way.

Re details.

  • I explicitly used $PSItem/$_ as parameter in setter (analogue of "value" keyword in C#).
  • I explicitly used method like return in getter

Re implementation. It was my first exercise. I did not know C# at all, do not know PowerShell insides, and haven't programmed for more than twenty years.

Very impressive! Bravo 馃檪

I explicitly used $PSItem/$_ as parameter in setter (analogue of "value" keyword in C#).

Yeah I went with $value but I'll definitely change that to dollar under. $value feels very out of place. I'm sort of inclined to go for all three, but that's almost definitely overkill.

I really like the design but I think it's conceptually more difficult than you might think. It took me a solid minute or so looking at the tests to understand that it's basically just altering the input/output of an auto backing field. I'm not sure if it's possible to use a lot of the patterns I want accessors for with that design.

I basically implemented a clone of C#'s accessor declarations. We can't define fields but hidden properties work just as well (mostly). It would be awesome if there was a way to do both though, maybe a keyword or attribute? It's hard to think of a pattern where that's easily understood.

I explicitly used method like return in getter

馃憤

I'm sort of inclined to go for all three, but that's almost definitely overkill.

I considered "$value" too and rejected because we all expect $_ in PowerShell and that's enough.

I really like the design but I think it's conceptually more difficult than you might think.

Sorry, I don't understand that you mean. Could you share more information and samples?

It would be awesome if there was a way to do both though, maybe a keyword or attribute? It's hard to think of a pattern where that's easily understood.

I stopped on this (and did not even bother to support "private").
It is fascinating, but it is not clear how necessary. Therefore, I stopped at the simplest case. For the same reason, I rejected parametric properties and indexers, although I initially implemented them.

Sorry, I don't understand that you mean. Could you share more information and samples?

From the tests:

class foo
{
    [string]$bar {
        set { return "---$PSItem---" }
        get { return "+++$PSItem+++" }
    }
}

I think that's a lot harder to understand what's going on versus this:

class foo
{
    hidden [string] $_bar

    [string] $bar {
        get { return '+++' + $this._bar + '+++' }
        set { $this._bar = "---$PSItem---" }
    }
}

Thanks for clarify. I see you point. Your option is easier to implement. I tried it and refused for the same reasons (harder to understand :-) ). In my opinion, this requires more user training and makes more mistakes. Also your option gives users too much freedom and provokes side effects.
In general, I believe that this backfield should appear in an explicit form only when it is really impossible to do without it - but we can.

@SeeminglyScience Have you other thoughts to share? I think we need to get a community feedback.

/cc @vors @lzybkr @mklement0 @rjmholt @daxian-dbw @SteveL-MSFT

Seeing a setter that returns feels off. It took me a little bit to get what was happening, and I imagine it would be harder for other folks who aren't as familiar with the behind the scenes. Speculation of course, could just be me.

Also your option gives users too much freedom and provokes side effects.

I think that type of freedom fits well in PowerShell. Even so, I don't think we need to be more strict than C#.

In general, I believe that this backfield should appear in an explicit form only when it is really impossible to do without it - but we can.

I can't think of many instances where I would implement an accessor that fits neatly into that mold. If it's not generated per call (where I wouldn't need backing field) or lazy init then I'm not likely to need accessors. I think your design is a really cool idea, and would be nice to have as an option, though it wouldn't be what I'd default to.

Wow! Really amazing you guys have tackled this. Feel like I'm quite late to this discussion.

I personally found the usage of $PSItem took me a while to understand in the setter example above. The get/set syntax being basically C# I think creates the expectation of C#-like semantics where you have to explicitly create your own backing property if you want something more than just assignment and dereference of a backing field.

The thing is that PowerShell classes don't really have fields — PS class properties with no get or set are equivalent to C# class properties with { get; set; } — so explicit get and set in PowerShell classes only really make sense with non-trivial bodies. But not all getters and setters need backing fields (they might be what EF calls a "computed property"), so $PSItem doesn't apply in all cases (not really a big problem -- this is true in other usages of $PSItem in PowerShell).

I think part of the issue is that having syntax so close to C#'s creates an expectation of everything being like C#, but because .NET has getters and setters baked into it I think the syntax proposed is the most sensible (get { ... } is not unlike begin { ... }). Which basically means I think we should go with @SeeminglyScience's version where $PSItem only applies as the value in a setter.

@SeeminglyScience / @iSazonov good work on this. I've been wanting this feature for a while. Do either of you have any updates on the progress of this feature?

I feel we don't get a progress until we design in-depth. Minor enhancements do not look so useful.

@ThomasNieto I stopped working on this awhile ago. The actual code is mostly done if anyone else wants to build off of it. I'm not going to have the spare time for the design debates/probably RFC, so anyone else who wants to take this on is welcome to.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aragula12 picture aragula12  路  3Comments

alx9r picture alx9r  路  3Comments

andschwa picture andschwa  路  3Comments

manofspirit picture manofspirit  路  3Comments

MaximoTrinidad picture MaximoTrinidad  路  3Comments