4

I have been unable to work with more than one custom attribute (decorator) on PowerShell 5.0 class method. In C#, I'm able do to following:

public class SomeAttribute : Attribute {
    public string Text { get; set; }
}

public class OtherAttribute : Attribute {
    public string Text { get; set; }
}

public class MyClass {
    [SomeAttribute(Text = "sometext")]
    [OtherAttribute(Text = "othertext")]
    public void MyMethod() {
        // ...
    }
}

and then somewhere else call

object[] customAttributes = typeof(MyClass).GetMethod("MyMethod").GetCustomAttributes(false);

which gives me an array of all custom attributes associated to the method without any problems.

In PowerShell 5.0, I'm trying to use analogically:

class SomeAttribute : Attribute {
    [string]$Text
}

class OtherAttribute : Attribute {
    [string]$Text
}

class MyClass {
    [SomeAttribute(Text="sometext")]
    [OtherAttribute(Text="othertext")]
    MyMethod() {
        # ...
    }
}

which seems to be the proper syntax as PowerShell happily accepts it. But listing the attributes via

[MyClass].GetMethod("MyMethod").GetCustomAttributes($false)

returns following error:

Exception calling "GetCustomAttributes" with "1" argument(s): "Could not load type 'OtherAttribute' from assembly '⧹powershell, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'."
At line:1 char:1
+ [MyClass].GetMethod("MyMethod").GetCustomAttributes($false)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : TypeLoadException

and

[MyClass].GetMethod("MyMethod").CustomAttributes

simply returns $null.

However when I use only one custom attribute, everything works as expected and the attribute is correctly returned using the snippets above.
How do I properly define multiple custom attributes for PowerShell 5.0 class method?

Update - Example of behavior using only one custom attribute

Let's assume only the first custom attribute from the original question.

class SomeAttribute : Attribute {
    [string]$Text
}

class MyClass {
    [SomeAttribute(Text="sometext")]
    MyMethod() {
        # ...
    }
}

Then

[MyClass].GetMethod("MyMethod").GetCustomAttributes($false)

gives following output

Text     TypeId
----     ------
sometext SomeAttribute

and

[MyClass].GetMethod("MyMethod").CustomAttributes

gives following output

AttributeType Constructor  ConstructorArguments NamedArguments
------------- -----------  -------------------- --------------
SomeAttribute Void .ctor() {}                   {Text = "sometext"}

Which leads me to believe that one attribute indeed works as expected.

2
  • Odd... when I attempt to execute your code just defining the two classes I get different behavior, specifically a runtime exception stating "An error occurred while creating the pipeline". If I remove the attribute on the second class, then it executes with no error. What specific PS version are you running ($host.Version) ? Mine is 5.0.10586.494. Commented Aug 20, 2016 at 1:01
  • Mine is 5.1.14393.0 (Win 10 RS1) Commented Aug 20, 2016 at 5:52

2 Answers 2

2

It seems that PowerShell (.NET, IMHO) have troubles referencing two different assemblies with same full name (something tell me, that it is not possible in .NET).

PS> class A {}
PS> class B {}
PS> [A].Assembly -eq [B].Assembly
False
PS> [A].Assembly.FullName -eq [B].Assembly.FullName
True
PS> class CA { [A] $A }; class CB { [B] $B }
PS> [CA]::new().A #work fine
PS> [CB]::new().B #error

Solution would be to define classes A and B in the same command, this would put them in the same generated assembly:

PS> class A {}; class B {}
PS> [A].Assembly -eq [B].Assembly
True
PS> class CA { [A] $A }; class CB { [B] $B }
PS> [CA]::new().A #work fine
PS> [CB]::new().B #work fine

So, this work fine for me:

PS> class SomeAttribute : Attribute {
>>>     [string]$Text
>>> }
>>> class OtherAttribute : Attribute {
>>>     [string]$Text
>>> }
PS> class MyClass {
>>>     [SomeAttribute(Text="sometext")]
>>>     [OtherAttribute(Text="othertext")]
>>>     MyMethod() {
>>>         # ...
>>>     }
>>> }
PS> [MyClass].GetMethod("MyMethod").GetCustomAttributes($false)

Text      TypeId
----      ------
sometext  SomeAttribute
othertext OtherAttribute

Other solution would be to not define classes in interactive session but in script files. In that case, obfuscated file name will be part of generated assembly name, thus you will not have this trouble even when attributes defines in different script files.

Sign up to request clarification or add additional context in comments.

2 Comments

Yup, this seems to be the case. Defining all attributes in one go works as well as defining them in separate script and including the script. However I cannot use the blank lines in interactive session to maintain the readability and I cannot use the attributes directly in the included script as I would be trying to reference an assembly which is not fully defined yet. Seems like PowerShell is not ready for C# like programming yet. Pity.
I cannot use the blank lines in interactive session to maintain the readability Shift+Enter work for me. I cannot use the attributes directly in the included script You can put attributes in separate .psm1 file and add using module .\MyAttributes.psm1 in the beginning of script file. It look like a bug for me, it should be possible to reference attributes defined in same script file.
0

I am skeptical that you really got the results you expected even using just one custom attribute. The reason is that methods do not support attributes, at least that I have found. A method supports an optional return type that, in PowerShell, happens to look just like an attribute. That is you can define a method returning void...

MyMethod() {
    $foo = 5        
}

or a method returning something (like an int in this case)...

[int] MyMethod() {
    $foo = 5
    return $foo
}

(I'm hoping that someone can prove me wrong, though, and show that PowerShell classes do support attributes!)

1 Comment

I've updated the question to show what kind of output I'm getting (and what leads me to believe that it works properly) when I use just one attribute. The problem is that I haven't been able to find any proper documentation for this either, so it's possible that it works with one attribute just by coincidence or I'm simply overlooking something fundamental.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.