Powershell允许使用一些C#作为Add-Type Cmdlet的参数创建自定义.NET类型。这是一个例子:
Add-Type @'
public class MyType1
{
public string a { get; set; }
public string b { get; set; }
}
'@
$obj1 = New-Object MyType1
$obj1.a = 'my a'
$obj1.b = 'my b'
PS C:\> $obj1 | fl *
a : my a
b : my b
假设我现在想要创建另一个类型,它本身具有类型MyType1
的属性。最明显的方法是使用Add-Type创建另一个自定义类型:
Add-Type @'
public class MyType2
{
public string c { get; set; }
public MyType1 subObj { get; set; }
}
'@
然而,这会导致以下错误:
The type or namespace name 'MyType1' could not be found (are you
missing a using directive or an assembly reference?)
如何在Powershell中创建嵌套的自定义.NET类型以便在Powershell中使用?
注意:我知道您可以使用New-Object PSObject
和Add-Member
创建嵌套对象。这些Cmdlet使用Powershell's Extended Type System并生成PSObjects
类型的对象。我正在使用.NET API,因此我需要创建特定的真正的 .NET对象。
答案 0 :(得分:1)
正如@ arco444所指出的,当您在Add-Type
的单个调用中定义父对象和子对象时,创建自定义嵌套.NET类型会起作用。这是什么样的:
Add-Type @'
public class MyType3a
{
public string a { get; set; }
public string b { get; set; }
}
public class MyType3
{
public string c { get; set; }
public MyType3a subObj { get; set; }
}
'@
$obj3a = New-Object MyType3a
$obj3a.a = 'my a'
$obj3a.b = 'my b'
$obj3 = New-Object MyType3
$obj3.subObj = $obj3a
PS C:\> $obj3.subObj | fl
a : my a
b : my b
答案 1 :(得分:1)
根据评论,我设法使用临时DLL程序集来实现这一点。这很难看,而且我确信有人能够更好地理解“引擎盖下”的内容,可以让我直截了当地改善答案。这是:
$TypeDef1= @'
namespace Mynamespace
{
public class MyType1
{
public string a { get; set; }
public string b { get; set; }
}
}
'@
$type1 = Add-Type $TypeDef1 -PassThru -OutputAssembly "c:\temp\my.dll"
$obj1 = New-Object Mynamespace.MyType1
$obj1.a = 'my a'
$obj1.b = 'my b'
$TypeDef2 = @'
namespace Mynamespace
{
public class MyType2
{
public string c { get; set; }
public Mynamespace.MyType1 subObj { get; set; }
}
}
'@
Add-Type -TypeDefinition $TypeDef2 -ReferencedAssemblies "c:\temp\my.dll"
$obj2 = New-Object Mynamespace.MyType2
$obj2.subObj = $obj1
基本上,第一次编译的结果(add-type)保存在DLL中,并且此DLL作为引用的程序集传递给第二个add类型语句。
据我所知,已经有一个由add-type语句创建的临时DLL,可以在$ type1.Module中看到,但我找不到在第二个type-add命令中引用它的方法。
修改强>
在试图弄清楚如何使这不那么“丑陋”时,我遇到了其他人试图在C#本地完成类似的任务。
In C#, how do you reference types from one in-memory assembly inside another?
C# - Referencing a type in a dynamically generated assembly
第二个链接指出了一种方法,可能只是一点点.NETish。
默认情况下,PowerShell Add-Type 命令执行.NET编译器,并将 GenerateInMemory 选项设置为 $ true 。
这会编译代码并将生成的类型加载到内存中,而不会保留已编译程序集的实际副本。需要一个程序集的副本来编译引用原始类型的其他类型。
解决这个问题的一种方法是编写我们自己的 New-Type 函数。这是 Add-Type cmdlet的简化版本,它使用 GenerateInMemory = $ false 执行编译器,并返回对已编译程序集的引用。 然后可以使用此引用来编译后续类型。
仍在磁盘上生成临时文件,但编译器至少会对进程和位置进行模糊处理。
以下是代码:
function New-Type {
param([string]$TypeDefinition,[string[]]$ReferencedAssemblies)
$CodeProvider = New-Object Microsoft.CSharp.CSharpCodeProvider
# Location for System.Management.Automation DLL
$dllName = [PsObject].Assembly.Location
$Parameters = New-Object System.CodeDom.Compiler.CompilerParameters
$RefAssemblies = @("System.dll", $dllName)
$Parameters.ReferencedAssemblies.AddRange($RefAssemblies)
if($ReferencedAssemblies) {
$Parameters.ReferencedAssemblies.AddRange($ReferencedAssemblies)
}
$Parameters.IncludeDebugInformation = $true
$Parameters.GenerateInMemory = $false # Do not compile in memory (generates a temp DLL file)
$Results = $CodeProvider.CompileAssemblyFromSource($Parameters, $TypeDefinition) #compile
if($Results.Errors.Count -gt 0) {
$Results.Errors | % { Write-Error ("{0}:`t{1}" -f $_.Line,$_.ErrorText) }
}
return $Results.CompiledAssembly # return info for the assembly
}
$TypeDef1= @'
public class MyType1
{
public string a { get; set; }
public string b { get; set; }
}
'@
$Asembly1 = New-Type $TypeDef1
$obj1 = New-Object MyType1
$obj1.a = 'my a'
$obj1.b = 'my b'
$TypeDef2 = @'
public class MyType2
{
public string c { get; set; }
public MyType1 subObj { get; set; }
}
'@
$Asembly2 = New-Type -TypeDefinition $TypeDef2 -ReferencedAssemblies $Asembly1.Location
$obj2 = New-Object MyType2
$obj2.subObj = $obj1