Passando dados para PSCmdlet de seu ArgumentTransformationAttribute
-
27-10-2019 - |
Pergunta
Geralmente, estou tentando criar um PSCmdlet
que usa um parâmetro de um tipo que implementa IDisposeable
e requer descarte para evitar o vazamento de recursos.Também gostaria de aceitar um string
para esse parâmetro e criar uma instância desse tipo; no entanto, se eu mesmo criar esse objeto, preciso descartá-lo antes de retornar de ProcessRecord
.
Estou usando um ArgumentTransformationAttribute
com meu parâmetro para construir meu objeto IDisposeable
a partir de uma string, mas não consigo encontrar nenhuma maneira de passar dados dessa classe para meu PSCmdlet
sobre se criei o objeto ou não.Por exemplo:
[Cmdlet("Get", "MyDisposeableName")]
public class GetMyDisposeableNameCommand : PSCmdlet
{
[Parameter(Mandatory = true, Position = 0), MyDisposeableTransformation]
public MyDisposeable MyDisposeable
{
get;
set;
}
protected override void ProcessRecord()
{
try
{
WriteObject(MyDisposeable.Name);
}
finally
{
/* Should only dispose MyDisposeable if we created it... */
MyDisposeable.Dispose();
}
}
}
class MyDisposeableTransformationAttribute : ArgumentTransformationAttribute
{
public override Object Transform(EngineIntrinsics engineIntrinsics, Object input)
{
if (input is PSObject && ((PSObject)input).BaseObject is MyDisposeable)
{
/* We were passed a MyDisposeable, we should not dispose it */
return ((PSObject)input).BaseObject;
}
/* We created a MyDisposeable, we *should* dispose it */
return new MyDisposeable(input.ToString());
}
}
Meu melhor palpite aqui é subclassificar meu MyDisposeableClass
apenas para marcar que ele precisa de descarte explícito, mas isso parece bastante hacky e, embora funcione neste caso, obviamente não funcionaria se eu quisesse lidar com uma classe selada.
Existe uma maneira melhor de fazer isso?
Solução 2
No final, eu simplesmente uso parâmetros que aceitam um tipo que envolve MyDisposeable
.Minha preocupação inicial em fazer isso era que usar um tipo interno para um parâmetro impactaria a acessibilidade das funções.(Talvez isso tenha um impacto negativo na documentação, mas em um cmdlet a documentação é totalmente controlada por um arquivo XML.)
Após alguns testes, não parece haver nenhum problema em usar uma classe interna para um parâmetro e apenas permitir que a transformação aceite tipos públicos.Então, simplesmente crio uma classe de wrapper:
public class MyDisposeableWrapper
{
public MyDisposeable MyDisposeable
{
get;
set;
}
public bool NeedsDisposed
{
get;
set;
}
public MyDisposeableWrapper(MyDisposeable myDisposeable, bool needsDisposed)
{
MyDisposeable = myDisposeable;
NeedsDisposed = needsDisposed;
}
}
E faça com que o parâmetro use isso.No atributo de transformação, basta definir NeedsDisposed
com base em se o parâmetro recebeu um MyDisposeable
ou construiu um.por exemplo:
if(input is MyDisposeable)
{
return new MyDisposeableWrapper((MyDisposeable) input, false);
}
else
{
/* construct MyDisposeable from the input */
return new MyDisposeableWrapper(myDisposeable, true);
}
Outras dicas
Em vez de criar subclasses, você pode adicionar uma propriedade à sua classe MyDisposable?
public class MyDisposable
{
...
public bool IsAttributeCreated { get; set; }
}
Então, em seu código de atributo
/* We created a MyDisposeable, we *should* dispose it */
return new MyDisposeable(input.ToString()){IsAttributeCreated=true};
Finalmente em seu bloco final
finally
{
/* Should only dispose MyDisposeable if we created it... */
if (MyDisposable.IsAttributeCreated)
MyDisposeable.Dispose();
}