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.
IEnumerable
means "collection of Object
s". 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?
}
}
}