Search code examples
c#.netforeachironruby

How do I make a dynamic .Net object enumerable from IronRuby?


I have an application that implements several objects in C# and then expects those objects to be usable from IronRuby by adding them to a Microsoft.Scripting.Hosting.ScriptScope. This works for the most part: I've learned how to implement the various members of DynamicObject to make things act correctly. But how do I make an object compatible with Ruby's for loops?

My C# object looks like this:

 public class TrialObject : DynamicObject, IEnumerable<int> {
    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
       if (indexes.Length != 1) return base.TryGetIndex(binder, indexes, out result);
       try {
          var index = (int)indexes[0];
          result = index;
          return true;
       } catch { return base.TryGetIndex(binder, indexes, out result); }
    }

    public int Length { get { return 3; } }

    public IEnumerator<int> GetEnumerator() {
       yield return 0;
       yield return 1;
       yield return 2;
    }

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

Then I try to use the object from ruby, like so.

trial.Length                   # returns 3
trial[0]                       # returns 0
trial[2]                       # returns 2
for t in trial do <stuff>; end #error: no block given

How do I adjust the trial object to allow it to work in a for loop? I know I could use "i in 0..trial.Length-1" as a hack around, but I'd rather fix the problem than introduce a hack.


Solution

  • After some trial and error, I learned that I could get the desired effect by adding an "each" method to my class.

    public class TrialObject : DynamicObject, IEnumerable<int> {
       ...
       public IEnumerable<BuildableObject> each() { return this; }
       ...
    }
    

    It seems as if ironruby uses the "each" method to implement its for. So by adding an each method, I can get this dynamic object to act like the IEnumerable it's trying to be.