web-dev-qa-db-fra.com

Comment ajouter conditionnellement une classe avec Add-Type -TypeDefinition si elle n'est pas déjà ajoutée?

Considérez l'extrait de code PowerShell suivant:

$csharpString = @"
using System;

public sealed class MyClass
{
    public MyClass() { }
    public override string ToString() {
        return "This is my class. There are many others " +
            "like it, but this one is mine.";
    }
}
"@
Add-Type -TypeDefinition $csharpString;
$myObject = New-Object MyClass
Write-Host $myObject.ToString();

Si je l'exécute plusieurs fois dans le même AppDomain (par exemple, exécutez le script deux fois dans powershell.exe ou powershell_ise.exe), j'obtiens l'erreur suivante:

Add-Type : Cannot add type. The type name 'MyClass' already exists.
At line:13 char:1
+ Add-Type -TypeDefinition $csharpString;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (MyClass:String) [Add-Type],
 Exception
    + FullyQualifiedErrorId :
 TYPE_ALREADY_EXISTS,Microsoft.PowerShell.Commands.AddTypeCommand

Comment encapsuler l'appel à Add-Type -TypeDefinition afin qu'il ne soit appelé qu'une seule fois?

44
Justin Dearing

Cette technique fonctionne bien pour moi:

if (-not ([System.Management.Automation.PSTypeName]'MyClass').Type)
{
    Add-Type -TypeDefinition 'public class MyClass { }'
}
  • Le nom du type peut être placé entre guillemets "MyClass", entre crochets [MyClass] ou les deux "[MyClass]" (v3 + uniquement).
  • La recherche de nom de type n'est pas sensible à la casse.
  • Vous devez utiliser le nom complet du type, sauf s'il fait partie de l'espace de noms System (par exemple [System.DateTime] peut être recherché via 'DateTime', mais [System.Reflection.Assembly] ne peut pas être recherché via 'Assembly' ).
  • Je n'ai testé cela que sous Win8.1; PowerShell v2, v3, v4.

En interne, la classe PSTypeName appelle la méthode LanguagePrimitives.ConvertStringToType () qui gère le gros du travail. Il met en cache la chaîne de recherche en cas de succès, afin que les recherches supplémentaires soient plus rapides.

Je n'ai pas confirmé si oui ou non des exceptions sont levées en interne comme mentionné par x0n et Justin D.

50
skataben

En fait, rien de tout cela n'est requis. Add-Type conserve un cache de tout code que vous lui soumettez, ainsi que le type résultant. Si vous appelez Add-Type deux fois avec le même code, cela ne dérangera pas de compiler le code et renverra simplement le type de la dernière fois.

Vous pouvez le vérifier en exécutant simplement un appel Add-Type deux fois de suite.

La raison pour laquelle vous avez reçu le message d'erreur dans l'exemple ci-dessus est que vous avez changé le code entre les appels en Add-Type. Bien que la solution ci-dessus fasse disparaître cette erreur dans cette situation, cela signifie également que vous travaillez avec une ancienne définition du type qui n'agit probablement pas comme vous le pensez.

21
LeeHolmes

Il y a une meilleure façon de le faire sans encourir le coût des exceptions:

if (-not ("MyClass" -as [type])) {
    add-type @"
        public class MyClass { }
"@
}

mise à jour: bien, apparemment les signaux PowerShell en interne avec une exception de toute façon. Il a une mauvaise habitude de le faire. L'interpréteur utilise SEH pour signaler avec les mots clés break et continue, par exemple.

18
x0n

La façon la plus simple de le faire est un bloc try/catch. Vous avez deux options pour ce faire:

  • try { [MyClass] | Out-Null } catch { Add-Type -TypeDefinition $csharpString; }
  • try { Add-Type -TypeDefinition $csharpString; } catch {}
4
Justin Dearing

De cette façon, aucune exception n'est levée, c'est juste une petite base lente sur le nombre d'assemblages chargés:

[bool]([appdomain]::CurrentDomain.GetAssemblies() | ? { $_.gettypes() -match 'myclass' })
3
CB.