This really drove me nuts for a few minutes.
I had a great set of classes set up and working to do some good stuff. Some of the good stuff included generating ScriptBlock objects to be executed on remote servers via WinRM. Pretty easy so far, but I found some duplication of code blocks in different methods which generated the ScriptBlock's, and I wanted to make use of inheritance to prevent duplication.As it turns out, this is not straightforward at all.
In a "normal" case, reaching outside of the ScriptBlock is accomplished with the majickal $Using variable, which provides references to members directly outside the scope of the block.
For example:
...
[String] $foo = "bar"
[ScriptBlock] $Script =
{
Write-Host $Using:foo
}
Invoke-Command -ComputerName 'some computer' -Credential $SomeCredential -ScriptBlock $Script
# Echo's bar to the command line
So let's say this is taking place inside an instance of class NoWorky, like this:
[String] $foo
class NoWorky : SomeParentClass
{
NoWorky([String] $foo : base($foo))
{
$this.foo = $foo
}
[ScriptBlock] makeFooer()
{
[ScriptBlock] $Script =
{
Write-Host $this.foo
# Doesn't work! Script block has no idea about $this!
}
return $Script
}
}
"Okay...", I said to myself. "Perhaps $Using:this.foo?"
[String] $foo
class NoWorky : SomeParentClass
{
NoWorky([String] $foo : base($foo))
{
$this.foo = $foo
}
[ScriptBlock] makeFooer()
{
[ScriptBlock] $Script =
{
Write-Host $Using:this.foo
# Doesn't work! Script block has no idea about $Using:this!
}
return $Script
}
}
Egad! What is one to do?
Enter this crap-hack
So back in the day, before ECMAScript got with the times, you had to do something like this to get the correct "this" to be the "this" in your closures:
var me = thisYep, that's right. I have to make a local reference of this, and then use that this, by referring to me. Are you throwing up in your mouth a little?
Sooooo, I wound up figuring out that the code needed to look like the following to work:
[String] $foo
class Worky : SomeParentClass
{
Worky([String] $foo : base($foo))
{
$this.foo = $foo
}
[ScriptBlock] makeFooer()
{
[String] $foo = $this.foo
[ScriptBlock] $Script =
{
Write-Host $Using:foo
# Works. Outputs bar to console when the returned script block is executed.
}
return $Script
}
}
So that's about it for now. I'm glad I found out how to do this, but I'm still not satisfied. (EVER!)
Next step is to create closures for the class methods, so I can call the methods within the ScriptBlock, without having to copy/paste blocks of code all over the place.
TTFN