Подтвердить что ты не робот

Как я могу условно добавить класс с Add-Type -TypeDefinition, если он уже не добавлен?

Рассмотрим следующий фрагмент PowerShell:

$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();

Если я запускаю его более одного раза в том же AppDomain (например, запустите script дважды в файле powershell.exe или powershell_ise.exe), я получаю следующую ошибку:

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

Как переносить вызов на Add-Type -TypeDefinition, чтобы его только один раз вызывал?

4b9b3361

Ответ 1

Эта техника хорошо работает для меня:

if (-not ([System.Management.Automation.PSTypeName]'MyClass').Type)
{
    Add-Type -TypeDefinition 'public class MyClass { }'
}
  • Название типа может быть заключено в кавычки "MyClass", квадратные скобки [MyClass] или оба "MyClass" (только v3 +).
  • Поиск имени типа не чувствителен к регистру.
  • Вы должны использовать полное имя типа, если оно не является частью пространства имен System (например, [System.DateTime] можно просмотреть с помощью "DateTime", но [System.Reflection.Assembly] не может быть просмотрена посредством 'Собрание').
  • Я тестировал это только в Win8.1; PowerShell v2, v3, v4.

Внутренне класс PSTypeName вызывает метод LanguagePrimitives.ConvertStringToType(), который обрабатывает тяжелый подъем. Он кэширует строку поиска при успешном завершении, поэтому дополнительные поисковые запросы быстрее.

Я не подтвердил, были ли исключены внутренние исключения, как упоминалось x0n и Justin D.

Ответ 2

Собственно, ничто из этого не требуется. Add-Type поддерживает кеш любого кода, который вы ему отправляете, вместе с результирующим типом. Если вы дважды вызываете "Add-Type" с тем же кодом, то он не будет беспокоить компиляцию кода и просто вернет тип с последнего раза.

Вы можете проверить это, просто дважды запустив вызов Add-Type в строке.

Причина, по которой вы получили сообщение об ошибке в приведенном выше примере, заключается в том, что вы изменили код между вызовами на Add-Type. Хотя вышеприведенное решение делает эту ошибку в этой ситуации, это также означает, что вы работаете со старым определением типа, который, вероятно, не действует так, как вы думаете.

Ответ 3

Там более удобный способ сделать это без учета стоимости исключений:

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

update: хорошо, очевидно, сигналы powershell внутри с исключением в любом случае. У этого есть плохая привычка делать это. Например, интерпретатор использует SEH для обозначения ключевых слов break и continue.

Ответ 4

Самый простой способ сделать это - блок try/catch. У вас есть два варианта для этого:

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

Ответ 5

Таким образом, исключение не выбрасывается, оно лишь немного медленнее основывается на количестве загруженных сборок:

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