set-StrictMode -Version 'Latest'
$PSDefaultParameterValues['*:ErrorAction'] = 'Stop'
class TestAttribute : System.Attribute {
}
class TestClass {
[int] [TestAttribute()] $i = 5
}
S C:\Users\scbaker> Import-Module E:\tmp\Test-Module2.psm1
Import-Module : The invoked member is not supported before the type is created.
At line:1 char:1
Scotia Baker:
Because class names aren't visible outside their defining module, the only workaround I've found is using Add-Type. Defining the attribute in another module using PS5 class support does not work due to lexical scoping of class names.
ISSUE:
A custom attribute class cannot be defined and used within the same PowerShell module.
IMPACT:
(1) Debug time diagnosing the issue. The import failure does not say what member or type is involved, making debugging difficult.
(2) An extra module must be created to define attribute classes regardless of logical code layout.
The primary impact is in the debugging time; an extra module may be ugly but it's easy enough. A fix for the issue is of course preferred.
In advance of a fix can the workaround be clarified?
the only workaround I've found is using Add-Type (zip of files attached).
I can't see an attachment.
(2) An extra module must be created to define attribute classes regardless of logical code layout.
I've tried several ways to get my [Attribute]-derived class to be used by my function without success (WindowsPowershell 5.1.17763). The only way that works for me is to paste the class definition into my shell, and import modules containing functions using the attribute class.
This is perfectly doable. I do this to great effect in PSKoans. However, it has some caveats.
You'll notice I duplicate the definition _with_ and _without_ the Attribute name suffix, one inheriting from the other.
I found that unlike in C# where you can _consistently_ drop the Attribute portion of a class name when actually using it as an attribute, you can't always do this in PS. I don't know why, but in some circumstances (e.g., class being available in _global_ scope) you can define a class called KoanAttribute and refer to it with [Koan()]. In others, you instead have to use the _actual_ name of the class. To get around this weirdness, I duplicate the class name with a short name and just use it to directly inherit the original class. Whichever it picks, I'll get it more or less correct. You have to be careful when examining attributes manually, if you ever need to do that, though.
I also use this for my transform attributes in PSWordCloud:
And its use adheres to the aforementioned restriction -- its _full_ name has to be used, not the short name lacking the Attribute suffix on the name in the definition.
@SteveL-MSFT @lzybkr I'm not sure why this happens, but it's weirdly inconsistent. As I mentioned, if the type is made available in global scope (e.g., by including it as a ScriptsToProcess file in the module manifest), then the attribute can be referred to as [SizeTransform()] instead of [SizeTransformAttribute()]
@vexx32 Thank you so much for the detailed and super-quick response.
I found that unlike in C# where you can consistently drop the Attribute portion of a class name when actually using it as an attribute, you can't always do this in PS.
This. It had always bothered me how the "magic" naming worked, and I was just pondering on this further when your post appeared. Your information has really helped me.
Glad I could help!
Yeah, the rules here are a bit funky, and the unclear errors aren't a lot of help either, unfortunately.
Most helpful comment
This is perfectly doable. I do this to great effect in PSKoans. However, it has some caveats.
https://github.com/vexx32/PSKoans/blob/eac7d8250dfe49e85e12b7432ad687dcf5470f78/PSKoans/PSKoans.psm1#L16-L26
You'll notice I duplicate the definition _with_ and _without_ the
Attributename suffix, one inheriting from the other.I found that unlike in C# where you can _consistently_ drop the
Attributeportion of a class name when actually using it as an attribute, you can't always do this in PS. I don't know why, but in some circumstances (e.g., class being available in _global_ scope) you can define a class calledKoanAttributeand refer to it with[Koan()]. In others, you instead have to use the _actual_ name of the class. To get around this weirdness, I duplicate the class name with a short name and just use it to directly inherit the original class. Whichever it picks, I'll get it more or less correct. You have to be careful when examining attributes manually, if you ever need to do that, though.I also use this for my transform attributes in PSWordCloud:
https://github.com/vexx32/PSWordCloud/blob/55723befc757eae27fb20dfb7dd273360c150655/PSWordCloud/Public/New-WordCloud.ps1#L7-L58
And its use adheres to the aforementioned restriction -- its _full_ name has to be used, not the short name lacking the
Attributesuffix on the name in the definition.https://github.com/vexx32/PSWordCloud/blob/55723befc757eae27fb20dfb7dd273360c150655/PSWordCloud/Public/New-WordCloud.ps1#L307-L326
@SteveL-MSFT @lzybkr I'm not sure why this happens, but it's weirdly inconsistent. As I mentioned, if the type is made available in global scope (e.g., by including it as a
ScriptsToProcessfile in the module manifest), then the attribute can be referred to as[SizeTransform()]instead of[SizeTransformAttribute()]