“New-Object WindowsPrincipal([WindowsIdentity]::GetCurrent())”和“[WindowsPrincipal] [WindowsIdentity]::GetCurrent()”之间的区别

Plu*_*ang 4 powershell

我正在尝试检查 Powerhell 脚本是否以管理员身份运行。在网上搜索后,我得到了一些运行良好的示例代码。
为了获取该WindowsPrincipal对象,我找到了两个示例代码,如下所示。

第一的:

New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
Run Code Online (Sandbox Code Playgroud)

第二:

[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
Run Code Online (Sandbox Code Playgroud)

第二个让我很困惑。

根据此页面,我知道这[ ]是一个强制转换运算符。根据此页面,PowerShell 是基于 .NET Framework 构建的。在我看来,这意味着上述两个PowerShell脚本可以转换为C#。
所以我尝试一下。
当我将第一个 PowerShell 脚本转换为 C# 时。它工作正常,如下所示。

## First PowerShell script
New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())

// C#
var wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
Run Code Online (Sandbox Code Playgroud)

但是当我尝试将第二个 PowerShell 脚本转换为 C# 时。我收到编译错误。IDE 告诉我WindowsIdentity不能转换为WindowsPrincipal.

## Second PowerShell script
[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()

// both C# code below cause compile error
var wp = System.Security.Principal.WindowsIdentity.GetCurrent() as System.Security.Principal.WindowsPrincipal;
var wp2 = (System.Security.Principal.WindowsPrincipal)System.Security.Principal.WindowsIdentity.GetCurrent();
Run Code Online (Sandbox Code Playgroud)

正如我在C#上尝试的那样,System.Security.Principal.WindowsIdentity类型不能直接转换为System.Security.Principal.WindowsPrincipal类型。但为什么第二个 PowerShell 脚本可用呢?
或者[ ]第二个PowerShell脚本中的运算符不是类型转换运算符?
也许这个运算符不仅仅是转换对象类型?
第一个 PowerShell 脚本和第二个 PowerShell 脚本有什么区别?
我还遗漏了其他东西吗?

HAL*_*256 5

TLDR:PowerShell 可以做Magic。C# 无法实现 Magic。

PowerShell 可以同时处理链锯、保龄球瓶和球。

C# 只能在提前定义的情况下才能玩弄球。尝试在杂耍例程中添加新的电锯会导致杂耍者(编译器)抱怨。


问题在于函数、对象类型之间的差异以及如何转换对象类型。

System.Security.Principal是基础 .NET 库。该库可由 C# 和 PowerShell 使用。

WindowsIdentity.GetCurrent()是库中的一个函数。

WindowsPrincipal是一个对象类型,例如stringor int

调用WindowsIdentity.GetCurrent()会返回一个WindowsIdentity对象,然后您可以在代码中使用该对象。

由于WindowsIdentity不一定是您想要使用的对象类型,因此我们希望使用对象WindowsPrincipal。不幸的是我们不能直接将 from 转换WindowsIdentityWindowsPrincipalobject。我们必须使用WindowsPrincipal构造函数。

New-Object在 PowerShell 中,您可以通过cmdlet 或首次使用变量来创建新对象。这种便利是因为 PowerShell 是一种脚本语言,其中使用变量(例如)$a = 1隐式创建新变量。例如

PS C:\> Get-Variable test
Get-Variable : Cannot find a variable with the name 'test'.
At line:1 char:1
+ Get-Variable test
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (test:String) [Get-Variable], ItemNotFoundException
    + FullyQualifiedErrorId : VariableNotFound,Microsoft.PowerShell.Commands.GetVariableCommand

PS C:\> $test = 1
PS C:\> Get-Variable test

Name                           Value
----                           -----
test                           1
Run Code Online (Sandbox Code Playgroud)

使用New-Object示例:

New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
Run Code Online (Sandbox Code Playgroud)

这是正确的做法。您正在创建一个类型为 的新对象WindowsPrincipal,并将 Windows 标识传递给构造函数。

第二种方法:

[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
Run Code Online (Sandbox Code Playgroud)

是“错误的”,因为它使用了强制转换,之前我们说过我们不能直接从WindowsIdentity对象强制转换WindowsPrincipal。那么这是如何运作的呢?嗯,我之前说过 PowerShell 具有 Magic 功能,我稍后会讲到这一点。首先让我们看看正确的C#代码:

在 C# 中调用 .NET Framework 特定函数在语法上与 PowerShell 不同。您收到的特定编译错误的原因是因为我们正在尝试转换对象,但我们不能。

这个例子:

using System.Security.Principal;
var wp = WindowsIdentity.GetCurrent() as WindowsPrincipal; // compile error
Run Code Online (Sandbox Code Playgroud)

编译器将其翻译为:

WindowsIdentity.GetCurrent()
Run Code Online (Sandbox Code Playgroud)

运行功能GetCurrent()

as WindowsPrincipal;
Run Code Online (Sandbox Code Playgroud)

预期返回类型为WindowsPrincipal<-- 编译时错误。编译错误是因为该函数没有返回类型WindowsPrincipal,而是返回类型WindowsIdentity

第二个示例也是一个不起作用的变体,因为它无法直接投射对象。例如

using System.Security.Principal;
var wp = (WindowsPrincipal) WindowsIdentity.GetCurrent(); // compile error
Run Code Online (Sandbox Code Playgroud)

编译器将其翻译为:

WindowsIdentity.GetCurrent()
Run Code Online (Sandbox Code Playgroud)

运行函数GetCurrent()并返回对象WindowsIdentity

(WindowsPrincipal) WindowsIdentity.GetCurrent();
Run Code Online (Sandbox Code Playgroud)

获取WindowsIdentity对象并直接将其转换为WindowsPrincipal类型,但它不能。

正确C#代码还需要new关键字是:

var wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
Run Code Online (Sandbox Code Playgroud)

编译器将其翻译为:

WindowsIdentity.GetCurrent()
Run Code Online (Sandbox Code Playgroud)

运行函数GetCurrent()并返回对象WindowsIdentity

new WindowsPrincipal(WindowsIdentity.GetCurrent())
Run Code Online (Sandbox Code Playgroud)

创建一个新WindowsPrincipal对象,并将该WindowsIdentity对象传递给构造函数。现在有效。

那么为什么第二个例子是这样的:

[Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
Run Code Online (Sandbox Code Playgroud)

在 PowerShell 中工作时做了一些“错误”的事情?因为 PowerShell 是一种脚本语言,并且是动态解释的,并且可以使用Magic,所以它可以解释上面的内容并执行一些类型转换 Magic Quote:

  1. 直接赋值。如果您的输入是可直接分配的,只需将您的输入转换为该类型即可。
  2. 基于语言的转换。当目标类型为 void、Boolean、String、Array、Hashtable、PSReference(即:[ref])、XmlDocument(即:[xml])时,会完成这些基于语言的转换。Delegate(以支持 ScriptBlock 到 Delegate 转换)和 Enum。
  3. 解析转换。如果目标类型定义了接受该输入的 Parse() 方法,请使用该方法。
  4. 静态创建转换。如果目标类型定义了接受该输入的静态 ::Create() 方法,请使用该方法。
  5. 构造函数转换。如果目标类型定义了接受您的输入的构造函数,请使用它。
  6. 演员转换。如果目标类型从源类型定义了隐式或显式强制转换运算符,请使用它。如果源类型定义了目标类型的隐式或显式强制转换运算符,请使用该运算符。
  7. IConvertible 转换。如果源类型定义了知道如何转换为目标类型的 IConvertible 实现,请使用它。
  8. I字典转换。如果源类型是 IDictionary(即:Hashtable),请尝试使用目标类型的默认构造函数创建目标类型的实例,然后使用 IDictionary 中的名称和值设置源对象的属性。
  9. PSObject 属性转换。如果源类型是 PSObject,请尝试使用目标类型的默认构造函数创建目标类型的实例,然后使用 PSObject 中的属性名称和值来设置源对象的属性。。如果名称映射到方法而不是属性,则使用该值作为其参数来调用该方法。
  10. TypeConverter转换。如果有已注册的 TypeConverter 或 PSTypeConverter 可以处理转换,请执行此操作。您可以通过 types.ps1xml 文件(请参阅:$pshome\Types.ps1xml)或通过 Update-TypeData 注册 TypeConverter。

基本上,每当您在 PowerShell 中进行类型转换时,它都会执行 Magic 并尝试每种方法来为您动态动态转换类型。这就是为什么它可以处理你向杂耍者扔新电锯或保龄球瓶的原因。PowerShell 可以解释为我们并不是试图将电锯转换成球,而是电锯只是另一个物体,而且我们已经知道如何处理物体。

这种解释不是 .NET 的一部分,因此 C# 无法做到这一点,我们必须显式定义所有内容,并正确执行所有操作。这意味着您可以将所有 C# 和 .NET 代码转换有效的 PowerShell 代码,但反之则不然。