在Matlab中迭代C#迭代器(IEnumerable)

5
我有一个返回非常大量对象的C#方法,这个方法将被Matlab使用。
namespace MyNameSpace{
    public static class MyClass{
        public static IEnumerable<MyDataObject> GetVeryLargeResponse(){
            while(CheckForSomeFunkyConditionThatsRarelyTrue()){
               yield return GetMyNextDataObject();
            }
            yield break;
        }
    }
}

在Matlab中,当我进行调用时

result = MyClass.GetVeryLargeResponse();

我期望的结果类型是 IEnumerable<MyDataObject>,这样我就可以通过调用result.GetEnumerator()来获取 Enumerator<MyDataObject>

然而,我得到的类型是 MyNameSpace.<GetVeryLargeResponse>d_3result,没有可用的 GetEnumerator() 方法。我看到 result 的一个超类是 System.Collections.Generic.IEnumerable<MyClass>
在 Matlab 中有没有一种方法可以迭代它,或者甚至将 result 转换为 IEnumerable<MyClass>
注:
1. 由于数据量大,转换为 Array / IList 等不可行。 2. 这不是关于特定于 IQueryable 的问题,因此与How can I iterate over a C# IEnumerable in Matlab? 不同。 3. 我使用的是 Matlab 2010b。
3个回答

5

结果确实有一个GetEnumerator()方法 - 只是可能是通过显式接口实现。

如果Matlab不愿意处理这个问题,您可以编写自己的映射类型和扩展方法,以使事情更简单:

public static class Extensions
{
    public static EnumerableWrapper<T> Wrap<T>(this IEnumerable<T> source)
    {
        return new EnumerableWrapper<T>(source);
    }
}

public class EnumerableWrapper<T> : IEnumerable<T>
{
    private readonly IEnumerable<T> source;

    public EnumerableWrapper(IEnumerable<T> source)
    {
        this.source = source;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return new EnumeratorWrapper<T>(source.GetEnumerator());
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class EnumeratorWrapper<T> : IEnumerator<T>
{
    private readonly IEnumerator<T> source;

    public EnumeratorWrapper(IEnumerator<T> source)
    {
        this.source = source;
    }

    public T Current { get { return source.Current; } }

    object IEnumerator.Current { get { return Current; } }

    public bool MoveNext()
    {
        return source.MoveNext();
    }

    public void Reset()
    {
        source.Reset();
    }

    public void Dispose()
    {
        source.Dispose();
    }

}

那么尝试一下:

result = MyClass.GetVeryLargeResponse().Wrap();

看起来很奇怪,Matlab竟然没有默认支持这个功能...


看看我的MATLAB解决方案。 - sean
抱歉提起一篇旧帖,但是使用 AsEnumerable 不就可以实现你在这里尝试做的事情了吗?而且不需要你创建一个包装类型。 - Servy
@Servy:回顾这些年,我不清楚我在这里做的事情为什么。看起来OP正在查看IEnumerable<T>的执行时类型,而不是编译时类型。我会稍微编辑一下... - Jon Skeet
@Servy:哦,也许这是Matlab的问题。我期望AsEnumerable()只会返回输入引用,因为在这里重要的是执行时类型,这并没有帮助。 - Jon Skeet
@JonSkeet 是的,这是一个Matlab问题,实际上如果您尝试调用AsEnumerable(),您会收到一个错误。我已经发布了使用显式转换的解决方案。 - sean
显示剩余2条评论

5
考虑以下示例:

MyClass.cs

namespace MyNameSpace {
    public class Person {
        public string name { get; set; }
        public int age { get; set; }
    }

    public class MyClass {
        private static List<Person> people = new List<Person> {
            new Person {name = "name1", age=10},
            new Person {name = "name2", age=20},
            new Person {name = "name3", age=30},
            new Person {name = "name4", age=40},
            new Person {name = "name5", age=50}
        };

        public static IEnumerable<Person> GetPeople() {
            foreach (var p in people) {
                yield return p;
            }
        }
    }
}

code.m

%# load my assembly
str = 'C:\path\to\MyNameSpace.dll';
NET.addAssembly(str);

obj = MyNameSpace.MyClass.GetPeople();   %# IEnumerable<Person>

%# call ToArray() extension method: this forces immediate query evaluation
NET.addAssembly('System.Core');          %# contains 'System.Linq' namespace
arr = NET.invokeGenericMethod('System.Linq.Enumerable', 'ToArray', ...
    {'MyNameSpace.Person'}, obj);

%# loop through results
for i=1:arr.Length
    fprintf('name=%s, age=%d\n', char(arr(i).name), int32(arr(i).age));
end

代码产生的输出如下:
name=name1, age=10
name=name2, age=20
name=name3, age=30
name=name4, age=40
name=name5, age=50

正如您所看到的,我确实将返回的对象转换为了一个数组Person[](我意识到您试图避免这种情况)。奇怪的是,如果我们查看类的层次结构:

>> superclasses(obj)
Superclasses for class MyNameSpace.<GetPeople>d__0:
    System.Object
    System.Collections.Generic.IEnumerable<MyNameSpace*Person>
    System.Collections.IEnumerable
    System.Collections.Generic.IEnumerator<MyNameSpace*Person>
    System.IDisposable
    System.Collections.IEnumerator
    handle

您可以看到 System.Collections.Generic.IEnumerator<Person>,但是该对象似乎没有公开继承的 GetEnumerator 方法:

>> methods(obj,'-full')

Methods for class MyNameSpace.<GetPeople>d__0:

MyNameSpace.<GetPeople>d__0 obj <GetPeople>d__0(int32 scalar <>1__state)
logical scalar RetVal Equals(MyNameSpace.<GetPeople>d__0 this, System.Object obj)
int32 scalar RetVal GetHashCode(MyNameSpace.<GetPeople>d__0 this)
System.Type RetVal GetType(MyNameSpace.<GetPeople>d__0 this)
System.String RetVal ToString(MyNameSpace.<GetPeople>d__0 this)
event.listener L addlistener(handle sources, char vector eventname, function_handle scalar callback)  % Inherited from handle
event.proplistener L addlistener(handle sources, meta.property properties, char vector eventname, function_handle scalar callback)  % Inherited from handle
event.proplistener L addlistener(handle sources, string propertyname, char vector eventname, function_handle scalar callback)  % Inherited from handle
event.proplistener L addlistener(handle sources, cell propertynames, char vector eventname, function_handle scalar callback)  % Inherited from handle
delete(handle obj)  % Inherited from handle
logical TF eq(A, B)  % Inherited from handle
handle HM findobj(handle H, varargin)  % Inherited from handle
meta.property prop findprop(handle scalar object, string propname)  % Inherited from handle
logical TF ge(A, B)  % Inherited from handle
logical TF gt(A, B)  % Inherited from handle
logical validity isvalid(handle obj)  % Inherited from handle
logical TF le(A, B)  % Inherited from handle
logical TF lt(A, B)  % Inherited from handle
logical TF ne(A, B)  % Inherited from handle
notify(handle sources, string eventname)  % Inherited from handle
notify(handle sources, string eventname, event.EventData scalar eventdata)  % Inherited from handle

是的,这正是导致问题的原因。正如@Jon Skeet所指出的那样,这可能是由于显式接口实现的IEnumerable<MyClass> - 这意味着我们需要在Matlab中明确将其转换为IEnumerable<MyClass>。不确定我们是否有办法在Matlab中做到这一点。 - YetAnotherUser
@YetAnotherUser 请查看我的答案,了解如何使用显式转换。 - sean

1

你需要使用显式转换。首先显式地将IEnumerable实例进行转换:

result = MyClass.GetVeryLargeResponse();
resultEnumerable = NET.explicitCast(result,'System.Collections.IEnumerable');

然后获取枚举器:

resultEnumerator = resultEnumerable.GetEnumerator();

然后显式地转换枚举器:

resultEnumerator = NET.explicitCast(resultEnumerator, 'System.Collections.IEnumerator');

然后您可以根据需要遍历该集合:

while (resultEnumerator.MoveNext)
    resultEnumerator.Current
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接