问题描述
我有一个返回 IEnumerable<T>
(或 IEnumerable
)的 API,它是在 C# 中使用 yield return
内部实现的。
一个简单的例子:
public class Class1
{
public IEnumerable<int> Enumerate()
{
yield return 1;
}
}
在 PowerShell 中,我无法对 IEnumerable<T>.GetEnumerator
方法返回的枚举调用 Enumerate()
:
$cls = New-Object Class1
$enumerable = $cls.Enumerate()
Write-Host $enumerable.GetType()
$enumerator = $enumerable.GetEnumerator()
它失败了:
找不到“GetEnumerator”的重载和参数计数:“0”。
$enumerable.GetType()
预期返回:
Class1+d__0
C# 中的相同代码按预期工作。
如果该方法是使用普通的 return
实现的:
return new[] { 1 };
那么在 PowerShell 中就没有问题了。
为什么 PowerShell 看不到 GetEnumerator()
?
我发现了这个有点相关的问题:PowerShell/GetEnumerator,但它并没有真正给我答案/解决方案(或者我没有看到)。
解释一下,为什么我要使用 IEnumerator
,而不是使用 PowerShell foreach
:我想使用从 Start-ThreadJob
开始的并行线程来处理可枚举。并且枚举是惰性且耗时的。因此,在枚举之前将结果收集到某个容器会导致性能下降。
解决方法
问题似乎是编译器生成的 IEnumerable<T>
类型实现了 IEnumerable
和 IEnumerator<T>
explicitly:
private sealed class <M>d__0 : IEnumerable<int>,IEnumerable,...
{
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
...
}
[DebuggerHidden]
IEnumerator<string> IEnumerable<int>.GetEnumerator()
{
...
}
...
}
后期绑定语言在 .NET 中显式实现的接口通常存在问题:毕竟您的枚举器属于 <M>d__0
类型,并且该类型实现了两个不同的 GetEnumerator()
方法,它们返回不同的东西。>
(通过调用返回 <M>d__0
的方法获得 IEnumerable<int>
的实例并不重要:后期绑定语言忘记了所有关于该位类型信息的信息)。
该语言需要提供特殊的语法,让您说出要调用的 GetEnumerator()
接口版本。例如 IronPython 将 let you say something like:
System.Collections.Generic.IEnumerable[int].GetEnumerator(enumerator)
我不知道有任何 PowerShell 语法可以做同样的事情(而且我的搜索没有找到任何东西),而是人们 seem to be using reflection:
[System.Collections.Generic.IEnumerable[int]].GetMethod("GetEnumerator").Invoke($enumerable,$Null)
,
补充@canton7 的回答:您可能希望修改 C# 代码以使用隐式实现,而不是在 PowerShell 中使用反射。如果您想继续使用 yield return
,您可以在生成的显式实现之上添加一个隐式实现接口的包装类。
您需要选择要隐式实现的接口,IEnumerable<T>
或 IEnumerable
。对于 PowerShell 中的使用,这可能无关紧要。下面的代码隐式地实现了 IEnumerable<T>
。
internal class ImplicitEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerable<T> _enumerable;
public ImplicitEnumerable(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
}
public IEnumerator<T> GetEnumerator()
{
return _enumerable.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
像这样使用它:
public class Class1
{
public IEnumerable<int> Enumerate()
{
return new ImplicitEnumerable<int>(DoEnumerate());
}
private IEnumerable<int> DoEnumerate()
{
yield return 1;
}
}