开发者

Iterate over C# Iterator (IEnumerable) in Matlab

开发者 https://www.devze.com 2023-03-18 13:52 出处:网络
I have a C# method that returns very large number of objects. This is to be consumed in Matlab. namespace MyNameSpace{

I have a C# method that returns very large number of objects. This is to be consumed in Matlab.

namespace MyNameSpace{
    public static class MyClass{
        public static IEnumerable<MyDataObject> GetVeryLargeResponse(){
            while(CheckForSomeFunkyConditionThatsRarelyTrue()){
               yield return GetMyNextDataObject();
            }
            yield break;
        }
    }
}

In Matlab when I make a call

result = MyClass.GetVeryLargeResponse();

I would expect result to be of type IEnumerable<MyDataObject>, so as to be able to get the Enumerator<MyDataObject> by calling result.GetEnumerator().

Where as I'm getting result which i开发者_StackOverflows of type MyNameSpace.<GetVeryLargeResponse>d_3 with no GetEnumerator() method available. I do see one of result's Super class being System.Collections.Generic.IEnumerable<MyClass>.

Is there a way I can iterate over this in Matlab or even to 'cast' result to IEnumerable<MyClass> in Matlab.

p.s.

  1. Converting to Array / IList etc is not feasible due to data volume
  2. This is not duplicate of How can I iterate over a C# IEnumerable in Matlab?, as that is dealing with IQueryable specifically.
  3. I'm using Matlab 2010b


The result does have a GetEnumerator() method - it's just may be implemented with explicit interface implementation.

If Matlab isn't willing to handle that, you could always write your own mapping type and an extension method to make things simpler:

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();
    }

}

Then try:

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

It seems very odd for Matlab not so support this out of the box though...


Consider the following example:

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

The code produces the output:

name=name1, age=10
name=name2, age=20
name=name3, age=30
name=name4, age=40
name=name5, age=50

As you can see, I did convert the returned object as an array Person[] (which I realize you were trying to avoid). The weird thing is if you if we look at the hierarchy of classes:

>> 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

you can see the System.Collections.Generic.IEnumerator<Person>, but somehow the object doesn't seem to expose the inherited GetEnumerator method:

>> 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


You need to use explicit casts. First explicitly cast the IEnumerable instance:

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

Then get the enumerator:

resultEnumerator = resultEnumerable.GetEnumerator();

Then explicitly cast the enumerator:

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

You can then iterate over the collection as desired:

while (resultEnumerator.MoveNext)
    resultEnumerator.Current
end
0

精彩评论

暂无评论...
验证码 换一张
取 消