Home > database >  How to make a Deconstructor for class that implements IEnumerable
How to make a Deconstructor for class that implements IEnumerable

Time:05-17

I have a class that implements IEnumerable. It contains a GetEnumerator() method and a private class that implements IEnumerator. The Current() method of that class returns an object that is a KeyValuePair, i.e (some code elided for clarity)

internal sealed class BlockSheet : IEnumerable
{
    public IEnumerator GetEnumerator ()
    {
        return new BlockSheetEnumerator (this);
    }

    private class BlockSheetEnumerator : IEnumerator
    {
        public object Current
        {
            get
            {
                KeyValuePair<Point, Actor> pair = (KeyValuePair<Point, Actor>)this.enumerator.Current;
                return pair;
            }
        }
    }
}

This works fine if I call as follows

foreach (KeyValuePair<Point, Actor> unit in blocksheet)

but if I try to use the implicit KeyValuePair deconstructor and write

foreach ((Point coordinate, Actor actor) in blocksheet)

then I get two errors:

Error CS1061: 'object' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) (CS1061)

Error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'object', with 2 out parameters and a void return type. (CS8129)

(This is the same error as, for example, Where's Deconstruct method of KeyValuePair<> struct? but I don't think that applies - see below)

I would have thought that I would get the deconstructor 'for free'. I've tried adding a Deconstruct method to both the BlockSheet class and to the BlockSheetEnumerator class, to no effect (and I don't feel like that makes sense, because neither class is being deconstructed).

I'm targeting .NET 5 with C# 9, using Visual Studio 2019 for Mac version 8.10.22 (build 11). I have, of course, successfully used a deconstructor with an object that is a Dictionary, to prove to myself that it's nothing to do with the language/.NET version.

CodePudding user response:

IEnumerable means "collection of Objects". So, inside the loop you got an object on each iteration and you actually try to deconstruct object, not KeyValuePair<TKey, TValue>. And you does not have appropriate deconstructor for the object.

To use a deconstruction, you need explicit and concrete type. To achieve this you can:

  • implement IEnumerable<KeyValuePair<Point, Actor>>/IEnumerator<KeyValuePair<Point, Actor>> for your object BlockSheet. By the way, having generic IEnumerable<T> implemented, you can easy implement non-generic IEnumerable via the generic one.

  • just use a cast foreach (var (key,val) in blocksheet.Cast<KeyValuePair<Point, Actor>>()){/*loop body*/}. But this does not looks like a best choice as for me.

  • you can also implement Deconstruct for Object, but you should figure out how it should behave for objects, other then desired KeyValuePair, so I strongly do not recommend this way

      public static class Ext
      {
          public static void Deconstruct(this Object obj, out Point a, out Actor b)
          {
              if(obj is KeyValuePair<Point, Actor> pair)
              {
                  (a,b)=pair;
              }
              else
              {
                  a=null;
                  b=null;
                  // what can we do here?
              }
          }
      }
    
  • Related