【问题标题】:`Add-Type` C# 6+ features throwing errors`Add-Type` C# 6+ 功能引发错误
【发布时间】:2019-03-18 01:57:29
【问题描述】:
我正在尝试使用以下命令编译powershell 中的c# 源代码:
Add-Type -ReferencedAssemblies $assemblies -TypeDefinition $source
但c#-6.0 功能不起作用,例如:
Add-Type : c:\Users\...\AppData\Local\Temp\2\d2q5hn5b.0.cs(101) : Unexpected character '$'
代码:
new Problem($"... ({identifier})", node)
我正在使用powershell-v5.1
有没有办法解决这个问题?
【问题讨论】:
标签:
c#
powershell
c#-6.0
powershell-v5.1
c#
powershell
roslyn
【解决方案2】:
为了扩展 Patrick Hoffmans's solution,我对在 unbob's solution 中使用反射方法有点不舒服,因为将来可能会中断。
我制定了以下 powershell 代码,而不是使用 .NET 命名的类和接口:
#requires -Version 5
# download https://www.nuget.org/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform/ and extract with 7-zip to a location, enter that location on the next line
$DotNetCodeDomLocation = 'C:\Utils\microsoft.codedom.providers.dotnetcompilerplatform.2.0.1'
Add-Type -Path "$DotNetCodeDomLocation\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll"
# using Invoke-Expression moves this class definition to runtime, so it will work after the add-type and the ps5 class interface implementation will succeed
# This uses the public interface ICompilerSettings instead of the private class CompilerSettings
Invoke-Expression -Command @"
class RoslynCompilerSettings : Microsoft.CodeDom.Providers.DotNetCompilerPlatform.ICompilerSettings
{
[string] get_CompilerFullPath()
{
return "$DotNetCodeDomLocation\tools\RoslynLatest\csc.exe"
}
[int] get_CompilerServerTimeToLive()
{
return 10
}
}
"@
$DotNetCodeDomProvider = [Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider]::new([RoslynCompilerSettings]::new())
然后可以在以下示例中使用它:
-
直接将类型添加到powershell 实例,带有程序集参考示例(需要roslyn 编译器和上面的代码与您的脚本捆绑在一起):
Add-Type -CodeDomProvider $DotNetCodeDomProvider -TypeDefinition $your_source_code_block -ReferencedAssemblies @([System.Reflection.Assembly]::GetAssembly([hashtable]).Location)
-
要将代码编译为dll 以便在未来/其他脚本中加载(只需要将生成的dll 文件与您的脚本捆绑在一起):
$DotNetAssemblyParameters = [System.CodeDom.Compiler.CompilerParameters]::new(
@([System.Reflection.Assembly]::GetAssembly([hashtable]).Location),
'path_and_name_for_saved.dll',
$false
)
# you can adjust more compilation settings here if you want, see
# https://docs.microsoft.com/en-us/dotnet/api/system.codedom.compiler.compilerparameters?view=netframework-4.7.2
$compilationResults = $DotNetCodeDomProvider.CompileAssemblyFromSource(
$DotNetAssemblyParameters,
$your_source_code_block
)
编译后的dll 然后可以与简单的Add-Type 一起使用:
Add-Type -Path 'path_and_name_for_saved.Dll'
这允许您在 powershell 中使用最新的 .NET 编译器,如果您捆绑了 CodeDomProvider dll 和 roslyn 编译器,则可以将其与主脚本内联,或者您可以编译 C#将代码输出到dll,因此不必在每次运行脚本时都重新编译,从而更容易移植并加快脚本运行时间。
【解决方案3】:
当我尝试霍夫曼先生的方法时,我收到以下错误:
Add-Type : 找不到路径 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\bin\roslyn\csc.exe' 的一部分。
user1676558 mentions two solutions:
-
NuGet package 包含针对此特定问题的修复程序
-
snippet of C# 反映了有问题的私有字段并修复它
作为一名(小联盟)PowerShell 黑客,我根据source code 的检查提出了自己的 PowerShellian 解决方案:
$dncpTypes = Add-Type -Path C:\<path where I put the dll>\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll -PassThru
$dncpTypTab = [ordered]@{}
$dncpTypes | %{$dncpTypTab[$_.Name] = $_}
$compSetCtor = $dncpTypTab.CompilerSettings.GetConstructor(@([string],[int]))
$compSettings = $compSetCtor.Invoke(@('C:\Program Files (x86)\MSBuild\14.0\Bin\csc.exe', 10))
$cscpOtherCtor = $dncpTypTab.CSharpCodeProvider.GetConstructor('NonPublic,Instance', $null, @($dncpTypTab.ICompilerSettings), $null)
$roslynProvider = $cscpOtherCtor.Invoke($compSettings)
不用说,关于这是否是一个错误,关于内胎有一些讨论。看起来该提供程序的目标是 ASP.NET 并且在那里做正确的事情。人们也对从哪里获取 csc.exe 意见不一。我怀疑这可能会继续是in flux。
[稍后编辑:在 VS2017 中,csc 似乎存在于 ${env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Roslyn\csc.exe。]