在使用 C# yield return 实现的 IEnumerable 上的 PowerShell 中使用 GetEnumerator
Using GetEnumerator in PowerShell on IEnumerable implemented using C# yield return
我有一个 API returns IEnumerable<T>
(或 IEnumerable
),它是在 C# 中使用 yield return
.[=28= 内部实现的]
一个简单的例子:
public class Class1
{
public IEnumerable<int> Enumerate()
{
yield return 1;
}
}
在 PowerShell 中,我无法对方法 Enumerate()
:
返回的可枚举调用 IEnumerable<T>.GetEnumerator
$cls = New-Object Class1
$enumerable = $cls.Enumerate()
Write-Host $enumerable.GetType()
$enumerator = $enumerable.GetEnumerator()
它失败了:
Cannot find an overload for "GetEnumerator" and the argument count: "0".
预期的$enumerable.GetType()
returns:
Class1+d__0
C# 中的相同代码按预期工作。
如果该方法是使用普通 return
:
return new[] { 1 };
那么PowerShell就没有问题了。
为什么 PowerShell 看不到 GetEnumerator()
?
我发现了这个有点相关的问题:PowerShell/GetEnumerator,但它并没有真正给我 answer/solution(或者我没有看到它)。
解释一下,为什么我要使用 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()
方法,其中 return 不同的东西。
(它通过调用一个 returned 一个 IEnumerable<int>
的方法获得了 <M>d__0
的实例并不重要:后期绑定语言完全忘记了那一点类型信息)。
该语言需要提供特殊的语法来让你说出你想调用哪个接口的 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
,您可以添加一个包装器 class,它在生成的显式实现之上隐式实现该接口。
您需要选择要隐式实现的接口,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;
}
}
不幸的是,反射方法对我不起作用。
但是我设法通过使用 LINQ 和 ToArray() 方法枚举了 IEnumerable,如下所示:
$array = [Linq.Enumerable]::ToArray($enumerable)
我认为反射不起作用的原因是因为在我的例子中 IEnumerable 的成员项是对象(自定义 class),而不是像 class 这样的基础 classes int] 或 [string]
我有一个 API returns IEnumerable<T>
(或 IEnumerable
),它是在 C# 中使用 yield return
.[=28= 内部实现的]
一个简单的例子:
public class Class1
{
public IEnumerable<int> Enumerate()
{
yield return 1;
}
}
在 PowerShell 中,我无法对方法 Enumerate()
:
IEnumerable<T>.GetEnumerator
$cls = New-Object Class1
$enumerable = $cls.Enumerate()
Write-Host $enumerable.GetType()
$enumerator = $enumerable.GetEnumerator()
它失败了:
Cannot find an overload for "GetEnumerator" and the argument count: "0".
预期的$enumerable.GetType()
returns:
Class1+d__0
C# 中的相同代码按预期工作。
如果该方法是使用普通 return
:
return new[] { 1 };
那么PowerShell就没有问题了。
为什么 PowerShell 看不到 GetEnumerator()
?
我发现了这个有点相关的问题:PowerShell/GetEnumerator,但它并没有真正给我 answer/solution(或者我没有看到它)。
解释一下,为什么我要使用 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()
方法,其中 return 不同的东西。
(它通过调用一个 returned 一个 IEnumerable<int>
的方法获得了 <M>d__0
的实例并不重要:后期绑定语言完全忘记了那一点类型信息)。
该语言需要提供特殊的语法来让你说出你想调用哪个接口的 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
,您可以添加一个包装器 class,它在生成的显式实现之上隐式实现该接口。
您需要选择要隐式实现的接口,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;
}
}
不幸的是,反射方法对我不起作用。
但是我设法通过使用 LINQ 和 ToArray() 方法枚举了 IEnumerable,如下所示:
$array = [Linq.Enumerable]::ToArray($enumerable)
我认为反射不起作用的原因是因为在我的例子中 IEnumerable 的成员项是对象(自定义 class),而不是像 class 这样的基础 classes int] 或 [string]