【问题标题】:Iterate over C# Iterator (IEnumerable) in Matlab在 Matlab 中迭代 C# 迭代器(IEnumerable)
【发布时间】:2016-01-26 22:52:26
【问题描述】:

我有一个返回大量对象的 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&lt;MyDataObject&gt; 类型,以便能够通过调用result.GetEnumerator() 获得Enumerator&lt;MyDataObject&gt;

当我得到result 时,它是MyNameSpace.&lt;GetVeryLargeResponse&gt;d_3 类型,没有可用的GetEnumerator() 方法。我确实看到result 的超级类之一是System.Collections.Generic.IEnumerable&lt;MyClass&gt;

有没有一种方法可以在 Matlab 中进行迭代,甚至可以在 Matlab 中将result“转换”为IEnumerable&lt;MyClass&gt;

附言

  1. 由于数据量大,无法转换为Array / IList
  2. 这不是How can I iterate over a C# IEnumerable in Matlab? 的重复,因为它专门处理IQueryable
  3. 我正在使用 Matlab 2010b

【问题讨论】:

    标签: c# .net matlab


    【解决方案1】:

    结果确实有一个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 不那么支持开箱即用,这似乎很奇怪......

    【讨论】:

    • 非常聪明的解决方案 - 虽然它也需要 Enumerator 的包装器,但它是正确的。我仍然很想知道 Matlab 中的解决方案是什么,因为 C# 代码可能并不总是在我们的控制范围内。
    • @YetAnotherUser:为了完整性,将为IEnumerator&lt;T&gt; 添加包装器。恐怕我从来没有用过 Matlab,所以无能为力......
    • 查看我的解决方案,了解如何在 MATLAB 中执行此操作。
    • 很抱歉提出了一个旧帖子,但 AsEnumerable 不会完成您在此处尝试执行的操作,而无需创建包装类型吗?
    • @Servy:这些年来回想起来,我不清楚为什么我在这里做了任何事情。看起来 OP 正在查看 IEnumerable&lt;T&gt; 的执行时类型,而不是编译时类型。我会稍微编辑一下...
    【解决方案2】:

    考虑以下示例:

    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&lt;Person&gt;,但不知何故,该对象似乎没有公开继承的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&lt;MyClass&gt; 由显式接口实现实现——这意味着我们需要在Matlab 中将其显式转换为IEnumerable&lt;MyClass&gt;。不确定我们是否有办法在 Matlab 中做到这一点。
    • @YetAnotherUser 查看我关于如何使用显式转换的答案。
    【解决方案3】:

    您需要使用显式强制转换。首先显式转换 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-28
      • 2020-08-22
      • 1970-01-01
      • 2014-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多