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 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 objectBlockSheet
. By the way, having genericIEnumerable<T>
implemented, you can easy implement non-genericIEnumerable
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
forObject
, but you should figure out how it should behave for objects, other then desiredKeyValuePair
, so I strongly do not recommend this waypublic 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? } } }