在PowerShell中等同于C#的"using"关键字?

87

在C#的.Net框架中,当我使用另一个对象时,通过使用using指令,我可以节省很多打字。

using FooCompany.Bar.Qux.Assembly.With.Ridiculous.Long.Namespace.I.Really.Mean.It;

...


  var blurb = new Thingamabob();

...

那么在Powershell中是否有类似的方法?我正在访问许多.NET对象,不想每次都键入相同的内容。

 $blurb = new-object FooCompany.Bar.Qux.Assembly.With.Ridiculous.Long.Namespace.I.Really.Mean.It.Thingamabob;

一直如此。


1
我知道这是一个老问题,但PowerShell 5引入了using语句。您可以利用它来使用.net命名空间或模块(这是导入自定义类的唯一方法之一)。语法是using namespace Name.Space.Hereusing module C:\Path\to\manifest。唯一的要求是它必须出现在脚本中的任何其他语句之前(甚至是param块)。 - Maximilian Burszley
9个回答

59

在命名空间级别上,实际上没有这样的功能。我经常将常用类型分配给变量,然后实例化它们:

$thingtype = [FooCompany.Bar.Qux.Assembly.With.Ridiculous.Long.Namespace.I.Really.Mean.It.Thingamabob];
$blurb = New-Object $thingtype.FullName

如果类型不会被重复使用,那么可能不值得这么做,但我相信这是你能做的最好的。


有时我需要将SecureStrings转换回纯文本。以下是有用的:$ns = [System.Runtime.InteropServices.Marshal] 然后你可以 $ns::PtrToStringAuto($ns::SecureStringToBSTR($ss)) - petrsnd
1
注意:查看最高评分答案的人请注意,PowerShell 5.0 已经修复了这个问题。 - Dave Markle
@petrsnd 或使用 ([pscredential]::new('+',$ss)).GetNetworkCredential().Password - Nicolas Melay

51

PowerShell 5.0(包含在WMF5或Windows 10及以上版本中)添加了using namespace结构到语言中。您可以在脚本中像这样使用它:

#Require -Version 5.0
using namespace FooCompany.Bar.Qux.Assembly.With.Ridiculous.Long.Namespace.I.Really.Mean.It
$blurb = [Thingamabob]::new()

(第一行的 #Require 语句并不是使用 using namespace 所必需的,但它会防止脚本在PS 4.0及以下版本中运行,因为在这些版本中使用 using namespace 是一个语法错误。)


2
那个 #Require 语句是一个很方便的东西,谢谢。 - Simon Elms
这在一般脚本中是有效的,但是在使用内联脚本的Azure DevOps PowerShell任务中,会提示“using不是第一个语句”。 - Andrii

12

请查看这篇撰写于几年前的博客文章:http://blogs.msdn.com/richardb/archive/2007/02/21/add-types-ps1-poor-man-s-using-for-powershell.aspx

以下是从那篇文章中摘录的add-types.ps1代码:

param(
    [string] $assemblyName = $(throw 'assemblyName is required'),
    [object] $object
)

process {
    if ($_) {
        $object = $_
    }

    if (! $object) {
        throw 'must pass an -object parameter or pipe one in'
    }

    # load the required dll
    $assembly = [System.Reflection.Assembly]::LoadWithPartialName($assemblyName)

    # add each type as a member property
    $assembly.GetTypes() | 
    where {$_.ispublic -and !$_.IsSubclassOf( [Exception] ) -and $_.name -notmatch "event"} | 
    foreach { 
        # avoid error messages in case it already exists
        if (! ($object | get-member $_.name)) {
            add-member noteproperty $_.name $_ -inputobject $object
        }
    }
}

而且,要使用它:

RICBERG470> $tfs | add-types "Microsoft.TeamFoundation.VersionControl.Client"
RICBERG470> $itemSpec = new-object $tfs.itemspec("$/foo", $tfs.RecursionType::none)

我所做的基本上是爬取非平凡类型的程序集,然后编写一个"构造函数",使用Add-Member将它们(以结构化方式)添加到我关心的对象中。

另请参阅这篇后续文章:http://richardberg.net/blog/?p=38


似乎需要使用程序集名称(例如mscorlib)而不是类型名称(例如System.IO.Path)。 - Micha Wiedenmann
如果您没有对象可以传递给函数,您可以使用 New-Object PSObject 创建一个对象。 - Micha Wiedenmann

8

这只是一个玩笑,玩笑而已...

$fullnames = New-Object ( [System.Collections.Generic.List``1].MakeGenericType( [String]) );

function using ( $name ) { 
foreach ( $type in [Reflection.Assembly]::LoadWithPartialName($name).GetTypes() )
    {
        $fullnames.Add($type.fullname);
    }
}

function new ( $name ) {
    $fullname = $fullnames -like "*.$name";
    return , (New-Object $fullname[0]);
}

using System.Windows.Forms
using FooCompany.Bar.Qux.Assembly.With.Ridiculous.Long.Namespace.I.Really.Mean.It
$a = new button
$b = new Thingamabob

有人能解释一下类型中的双重“1”吗?我很难搜索到相关信息。 - Pete
这相当于C#中的using语句,而不是using指令。 - Paulo Morgado
我喜欢这个的外观。如果你承诺它,对我来说,这似乎是一个相当优雅的解决方案。PowerShell在构建新语法方面的灵活性让我非常高兴。 - Programmer Paul
@ProgrammerPaul 如果你觉得这很有趣,那就试试一下函数式编程语言——其中一些实现了try..catch作为,包括Haskell和Clojure。 - jpaugh
1
@ProgrammerPaul(关于函数式语言)哈哈,我正在阅读这篇文章,同时在使用F#库(通过FParsec进行DSL解析)。我了解并喜欢Haskell、Clojure、Scala,但仍然无法抑制评论的冲动。来吧:也许尝试一些真正面向对象的语言,比如Smalltalk——它将If/Else实现为一个库;-))) - Miloslav Raus

5

以下是适用于PowerShell 2.0的代码,用于添加类型别名。但问题在于它没有作用域。如果进行一些额外的工作,您可以“取消导入”命名空间,但这应该能让您有一个良好的开端。

##############################################################################
#.SYNOPSIS
# Add a type accelerator to the current session.
#
#.DESCRIPTION
# The Add-TypeAccelerator function allows you to add a simple type accelerator
# (like [regex]) for a longer type (like [System.Text.RegularExpressions.Regex]).
#
#.PARAMETER Name
# The short form accelerator should be just the name you want to use (without
# square brackets).
#
#.PARAMETER Type
# The type you want the accelerator to accelerate.
#
#.PARAMETER Force
# Overwrites any existing type alias.
#
#.EXAMPLE
# Add-TypeAccelerator List "System.Collections.Generic.List``1"
# $MyList = New-Object List[String]
##############################################################################
function Add-TypeAccelerator {

    [CmdletBinding()]
    param(

        [Parameter(Position=1,Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
        [String[]]$Name,

        [Parameter(Position=2,Mandatory=$true,ValueFromPipeline=$true)]
        [Type]$Type,

        [Parameter()]
        [Switch]$Force

    )

    process {

        $TypeAccelerators = [Type]::GetType('System.Management.Automation.TypeAccelerators')

        foreach ($a in $Name) {
            if ( $TypeAccelerators::Get.ContainsKey($a) ) {
                if ( $Force ) {
                    $TypeAccelerators::Remove($a) | Out-Null
                    $TypeAccelerators::Add($a,$Type)
                }
                elseif ( $Type -ne $TypeAccelerators::Get[$a] ) {
                    Write-Error "$a is already mapped to $($TypeAccelerators::Get[$a])"
                }
            }
            else {
                $TypeAccelerators::Add($a, $Type)
            }
        }

    }

}

Josh,谢谢,我还没有考虑过扩展类型加速器。这非常有趣,我想我会稍微尝试一下。 - froh42
这很方便,但使用时要小心。没有比编写一个在别人的机器上无法运行的脚本更糟糕的事情了。因此,我从不将这些加速器放在我的配置文件中。如果有什么问题,我会将它们放在脚本的顶部,这样它就会立即在 Add-TypeAccelerator 处失败。 - Josh

5

如果您只需要创建一个实例,可以将长命名空间的名称存储在字符串中:

$st = "System.Text"
$sb = New-Object "$st.StringBuilder"

虽然它不像C#中的using指令那样强大,但至少非常易于使用。


2
谢谢大家的贡献。我将Richard Berg的回答标记为答案,因为它最接近我要找的内容。
你们所有人的回答都让我找到了最有希望的方向:在他的博客文章中,Keith Dahlby提出了一个Get-Type命令,可以轻松构建通用方法的类型。
我认为没有理由不将其扩展为在预定义的程序集路径中搜索类型。
免责声明:我还没有建立这个...
以下是如何使用它的方法:
$path = (System.Collections.Generic, FooCompany.Bar.Qux.Assembly.With.Ridiculous.Long.Namespace.I.Really.Mean.It)

$type = get-type -Path $path List Thingamabob
$obj = new-object $type
$obj.GetType()

这将导致一个很好的通用Thingamabob列表。当然,除了路径定义之外的所有内容都会被包装到另一个实用函数中。扩展的get-type将包括一步来解析任何给定类型与路径的匹配。

0
#Requires -Version 5
using namespace System.Management.Automation.Host
#using module

-2

2
这将相当于 C# 中的 using 语句,而不是 using 指令。 - Paulo Morgado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接