50

考虑以下 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 中多次运行它(例如在 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 的调用,使其仅调用一次?

4

5 回答 5

56

这种技术对我很有效:

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] 不能通过 'Assembly' 查找)。
  • 我只在 Win8.1 中测试过;PowerShell v2、v3、v4。

在内部,PSTypeName 类调用处理繁重工作的 LanguagePrimitives.ConvertStringToType() 方法。它在成功时缓存查找字符串,因此额外的查找速度更快。

我还没有确认 x0n 和 Justin D 提到的内部是否引发了任何异常。

于 2014-03-03T20:34:49.723 回答
24

实际上,这些都不是必需的。Add-Type 维护您提交给它的任何代码的缓存,以及生成的类型。如果您使用相同的代码调用 Add-Type 两次,那么它不会费心编译代码,只会返回上次的类型。

您可以通过连续两次运行 Add-Type 调用来验证这一点。

您在上面的示例中收到错误消息的原因是您在调用 Add-Type 之间更改了代码。虽然上面的解决方案在这种情况下使该错误消失,但这也意味着您正在使用可能与您认为的方式不同的类型的较旧定义。

于 2014-03-03T23:42:55.843 回答
21

有一种更好的方法可以做到这一点而不会产生异常成本

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

更新:好吧,显然powershell在内部发出信号,但无论如何都有异常。它有这样做的坏习惯。例如,解释器使用 SEH 用breakandcontinue关键字发出信号。

于 2013-05-14T21:06:39.353 回答
4

最简单的方法是使用 try/catch 块。您有两种选择:

  • try { [MyClass] | Out-Null } catch { Add-Type -TypeDefinition $csharpString; }
  • try { Add-Type -TypeDefinition $csharpString; } catch {}
于 2013-05-14T21:02:04.543 回答
4

这样就不会抛出异常,只是根据加载的程序集数量有点慢:

[bool]([appdomain]::CurrentDomain.GetAssemblies() | ? { $_.gettypes() -match 'myclass' })
于 2013-05-15T12:40:05.007 回答