在 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.StackTraceSystem.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))