在 PowerShell 中提供 C# 抽象 class 的实现
Provide an implementation of a C# abstract class in PowerShell
我正在尝试创建 abstract class' 函数的 PowerShell-class 实现:
class ReadList : Intacct.SDK.Functions.AbstractFunction {
[string]$ObjectName
ReadList ([string]$ObjectName) {
$this.ObjectName = $ObjectName
}
[void] WriteXml ( [Intacct.SDK.Xml.IaXmlWriter]$xml ) {
$xml.WriteStartDocument()
$xml.WriteStartElement("get_list")
$xml.WriteAttributeString("object",$this.ObjectName)
$xml.WriteEndElement() # </get_list>
$xml.WriteEndDocument()
$xml.Flush()
$xml.Close()
}
}
当我尝试使用它时,出现 运行 次异常:
Error during creation of type "ReadList". Error message: Method 'WriteXml' in type 'ReadList' from assembly 'PowerShell Class Assembly, Version=1.0.0.1,
| Culture=neutral, PublicKeyToken=null' does not have an implementation.
我试过添加 ref
标签:
[void] WriteXml ( [Intacct.SDK.Xml.IaXmlWriter][ref]$xml ) {
但是我得到了一个不同的错误:
Multiple type constraints are not allowed on a method parameter.
是我做错了什么,还是我想做的事情不受支持?
据我所知,这是不支持的。我一直没能找到好的解决方法,也找不到能简明解释它的资源。
从技术上讲,第二种实现是正确的,但根据错误,PowerShell 不支持方法参数的多种类型约束[1].
通常这不是世界末日。问题是当从抽象 class 继承时, class 必须定义所有抽象方法 [2],所以带有签名的方法 WriteXml (ref IaXmlWriter <ParameterName>)
必须定义。其他任何东西都会给出 Method 'WriteXml' ... does not have an implementation.
即使 PowerShell 支持多种类型约束,我也不认为这会按预期工作,因为 c#
与 PowerShell 的 [ref]
不同 - [ref]
是为支持 COM 对象,文档明确指出它不能用于类型转换 class 成员[3].
我知道的唯一可行的解决方法是在 c#
中编写 ReadList
class 定义,然后通过 Add-Type
[= 添加到 PowerShell 74=][4]...不太好。
这一切都超出了我的知识范围;也许有一个好的解决方案,有更多知识的人可以纠正我。
参考资料
[1] SO post that touches on this and [ref](下同)
查看 $Error.Exception.StackTrace
,System.Management.Automation.ScriptBlock.Create
is raising the exception. Couldn't go further with PowerShell 5.1 but PowerShell Core files include a check for class method parameters here which points to the MultipleTypeConstraintsOnMethodParam
引发的异常。
Scripting.Classes.BasicParsing.Tests.ps1
检查多个类型将引发 MultipleTypeConstraintsOnMethodParam
异常。
[2] 派生的 class 抽象 class 必须实现所有抽象方法。
Abstract and Sealed Classes and Class Members
[3] SO post that touches on this and multiple type constraints(同上)
class 个成员不支持 PSReference 类型
about_Classes
[4] Add a .NET type to a session
编辑
详细说明 Add-Type
解决方案
- 使用
ReferencedAssemblies
传入Intacct.SDK.dll
和依赖程序集。
- 使用
[Reflection.Assembly]
加载这些程序集
指定版本时使用 Load()
或仅使用名称 LoadFromPartialName()
,指定路径时使用 LoadFrom()
。
$Assemblies = @(
'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
'System.Xml.ReaderWriter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
) |
foreach {
[Reflection.Assembly]::Load($_)
}
$Assemblies += @(
"$pwd/Microsoft.Extensions.Logging.Abstractions.dll"
"$pwd/Intacct.SDK.dll"
) | foreach {
[Reflection.Assembly]::LoadFrom($_)
}
Add-Type -TypeDefinition $Source -ReferencedAssemblies $Assemblies
我得出了同样的结论,我需要创建一个 C# class 定义并导入它。
Push-Location ~/Desktop
$Source = @"
using Intacct.SDK.Functions;
using Intacct.SDK.Xml;
public class GetList : Intacct.SDK.Functions.AbstractFunction
{
public string ObjectName;
public GetList (string objectName)
{
this.ObjectName = objectName;
}
public override void WriteXml( ref Intacct.SDK.Xml.IaXmlWriter xml )
{
xml.WriteStartDocument();
xml.WriteStartElement("get_list");
xml.WriteEndDocument();
xml.Flush();
}
}
"@
# netstandard and ReaderWriter are located in `$PSHOME`; others on Desktop
$Assemblies = @(
'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
'System.Xml.ReaderWriter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
"$pwd/Microsoft.Extensions.Logging.Abstractions.dll"
"$pwd/Intacct.SDK.dll"
)
# using ReferenceAssemblies based on a suggestion from the PowerShell Slack channel
$Type = Add-Type -TypeDefinition $Source -ReferencedAssemblies $Assemblies -PassThru
$GetList = [GetList]::new('something')
$GetList
产生错误:
Add-Type: getlist.ps1:37:9
Line |
37 | $Type = Add-Type -TypeDefinition $Source -ReferencedAssemblies $Assem …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Unable to load one or more of the requested types. Could not load file or assembly 'Intacct.SDK, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. The parameter is incorrect. (0x80070057 (E_INVALIDARG))
我正在尝试创建 abstract class' 函数的 PowerShell-class 实现:
class ReadList : Intacct.SDK.Functions.AbstractFunction {
[string]$ObjectName
ReadList ([string]$ObjectName) {
$this.ObjectName = $ObjectName
}
[void] WriteXml ( [Intacct.SDK.Xml.IaXmlWriter]$xml ) {
$xml.WriteStartDocument()
$xml.WriteStartElement("get_list")
$xml.WriteAttributeString("object",$this.ObjectName)
$xml.WriteEndElement() # </get_list>
$xml.WriteEndDocument()
$xml.Flush()
$xml.Close()
}
}
当我尝试使用它时,出现 运行 次异常:
Error during creation of type "ReadList". Error message: Method 'WriteXml' in type 'ReadList' from assembly 'PowerShell Class Assembly, Version=1.0.0.1, | Culture=neutral, PublicKeyToken=null' does not have an implementation.
我试过添加 ref
标签:
[void] WriteXml ( [Intacct.SDK.Xml.IaXmlWriter][ref]$xml ) {
但是我得到了一个不同的错误:
Multiple type constraints are not allowed on a method parameter.
是我做错了什么,还是我想做的事情不受支持?
据我所知,这是不支持的。我一直没能找到好的解决方法,也找不到能简明解释它的资源。
从技术上讲,第二种实现是正确的,但根据错误,PowerShell 不支持方法参数的多种类型约束[1].
通常这不是世界末日。问题是当从抽象 class 继承时, class 必须定义所有抽象方法 [2],所以带有签名的方法 WriteXml (ref IaXmlWriter <ParameterName>)
必须定义。其他任何东西都会给出 Method 'WriteXml' ... does not have an implementation.
即使 PowerShell 支持多种类型约束,我也不认为这会按预期工作,因为 c#
与 PowerShell 的 [ref]
不同 - [ref]
是为支持 COM 对象,文档明确指出它不能用于类型转换 class 成员[3].
我知道的唯一可行的解决方法是在 c#
中编写 ReadList
class 定义,然后通过 Add-Type
[= 添加到 PowerShell 74=][4]...不太好。
这一切都超出了我的知识范围;也许有一个好的解决方案,有更多知识的人可以纠正我。
参考资料
[1] SO post that touches on this and [ref](下同)
查看 $Error.Exception.StackTrace
,System.Management.Automation.ScriptBlock.Create
is raising the exception. Couldn't go further with PowerShell 5.1 but PowerShell Core files include a check for class method parameters here which points to the MultipleTypeConstraintsOnMethodParam
引发的异常。
Scripting.Classes.BasicParsing.Tests.ps1
检查多个类型将引发 MultipleTypeConstraintsOnMethodParam
异常。
[2] 派生的 class 抽象 class 必须实现所有抽象方法。
Abstract and Sealed Classes and Class Members
[3] SO post that touches on this and multiple type constraints(同上)
class 个成员不支持 PSReference 类型
about_Classes
[4] Add a .NET type to a session
编辑
详细说明 Add-Type
解决方案
- 使用
ReferencedAssemblies
传入Intacct.SDK.dll
和依赖程序集。 - 使用
[Reflection.Assembly]
加载这些程序集
指定版本时使用 Load()
或仅使用名称 LoadFromPartialName()
,指定路径时使用 LoadFrom()
。
$Assemblies = @(
'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
'System.Xml.ReaderWriter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
) |
foreach {
[Reflection.Assembly]::Load($_)
}
$Assemblies += @(
"$pwd/Microsoft.Extensions.Logging.Abstractions.dll"
"$pwd/Intacct.SDK.dll"
) | foreach {
[Reflection.Assembly]::LoadFrom($_)
}
Add-Type -TypeDefinition $Source -ReferencedAssemblies $Assemblies
我得出了同样的结论,我需要创建一个 C# class 定义并导入它。
Push-Location ~/Desktop
$Source = @"
using Intacct.SDK.Functions;
using Intacct.SDK.Xml;
public class GetList : Intacct.SDK.Functions.AbstractFunction
{
public string ObjectName;
public GetList (string objectName)
{
this.ObjectName = objectName;
}
public override void WriteXml( ref Intacct.SDK.Xml.IaXmlWriter xml )
{
xml.WriteStartDocument();
xml.WriteStartElement("get_list");
xml.WriteEndDocument();
xml.Flush();
}
}
"@
# netstandard and ReaderWriter are located in `$PSHOME`; others on Desktop
$Assemblies = @(
'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
'System.Xml.ReaderWriter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
"$pwd/Microsoft.Extensions.Logging.Abstractions.dll"
"$pwd/Intacct.SDK.dll"
)
# using ReferenceAssemblies based on a suggestion from the PowerShell Slack channel
$Type = Add-Type -TypeDefinition $Source -ReferencedAssemblies $Assemblies -PassThru
$GetList = [GetList]::new('something')
$GetList
产生错误:
Add-Type: getlist.ps1:37:9
Line |
37 | $Type = Add-Type -TypeDefinition $Source -ReferencedAssemblies $Assem …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Unable to load one or more of the requested types. Could not load file or assembly 'Intacct.SDK, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. The parameter is incorrect. (0x80070057 (E_INVALIDARG))