No he podido trabajar con más de un atributo personalizado (decorador) en el método de clase PowerShell 5.0. En C #, puedo hacer lo siguiente:

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() {
        // ...
    }
}

Y luego llamar a otro lugar

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

Lo que me da una matriz de todos los atributos personalizados asociados al método sin ningún problema.

En PowerShell 5.0, estoy tratando de usar analógicamente:

class SomeAttribute : Attribute {
    [string]$Text
}

class OtherAttribute : Attribute {
    [string]$Text
}

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

Que parece ser la sintaxis adecuada ya que PowerShell la acepta felizmente. Pero enumerando los atributos a través de

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

Devuelve el siguiente 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

Y

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

Simplemente devuelve $null.

Sin embargo, cuando utilizo solo un atributo personalizado, todo funciona como se esperaba y el atributo se devuelve correctamente utilizando los fragmentos anteriores.
¿Cómo defino correctamente varios atributos personalizados para el método de clase de PowerShell 5.0?

Actualización: ejemplo de comportamiento utilizando solo un atributo personalizado

Supongamos solo el primer atributo personalizado de la pregunta original.

class SomeAttribute : Attribute {
    [string]$Text
}

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

Entonces

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

Da el siguiente resultado

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

Y

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

Da el siguiente resultado

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

Lo que me lleva a creer que un atributo funciona como se esperaba.

4
Disassembler 18 ago. 2016 a las 22:52

2 respuestas

La mejor respuesta

Parece que PowerShell (.NET, en mi humilde opinión) tiene problemas para hacer referencia a dos ensamblados diferentes con el mismo nombre completo (algo me dice que no es posible en .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

La solución sería definir las clases A y B en el mismo comando, esto las pondría en el mismo ensamblado generado:

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

Entonces, esto funciona bien para mí:

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

Otra solución sería no definir clases en sesiones interactivas sino en archivos de script. En ese caso, el nombre de archivo ofuscado será parte del nombre de ensamblado generado, por lo que no tendrá este problema incluso cuando los atributos se definan en diferentes archivos de script.

2
user4003407 20 ago. 2016 a las 05:59

Soy escéptico de que realmente haya obtenido los resultados que esperaba incluso usando solo un atributo personalizado. La razón es que los métodos no admiten atributos, al menos eso lo he encontrado. Un método admite un tipo de retorno opcional que, en PowerShell, parece un atributo . Es decir, puede definir un método que devuelva vacío ...

MyMethod() {
    $foo = 5        
}

O un método que devuelve algo (como un int en este caso) ...

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

(Sin embargo, espero que alguien pueda demostrar que estoy equivocado y demostrar que las clases de PowerShell admiten atributos).

0
Michael Sorens 19 ago. 2016 a las 17:08