So I'm trying to figure out if some any random IEnumerable passed to my method has been grouped. The idea being that I'm going to handle grouped data differently. My problem is the IGrouping<>
is a generic interface that doesn't implement a non-generic interface, and for whatever reason I can't test for it with is
statements.
To test this out here is my sample code:
var dict = new Dictionary<int, string>() {
{1, "Prime" },
{2, "Prime" },
{3, "Prime" },
{4, "Not" },
{5, "Prime" },
{6, "Not" },
{7, "Prime" },
{8, "Not" },
{9, "Not" },
{10, "Not" },
{11, "Prime" }
};
var grouped = from d in dict
group d by d.Value into gd
select gd;
Debug.WriteLine("IEnumerable<IGrouping<string, KeyValuePair<int, string>>>: {0}", grouped is IEnumerable<IGrouping<string, KeyValuePair<int, string>>>);
Debug.WriteLine("IEnumerable<IGrouping<object, KeyValuePair<int, string>>>: {0}", grouped is IEnumerable<IGrouping<object, KeyValuePair<int, string>>>);
Debug.WriteLine("IEnumerable<IGrouping<object, KeyValuePair<object, object>>: {0}", grouped is IEnumerable<IGrouping<object, object>>);
Debug.WriteLine("IEnumerable<IGrouping<object, object>: {0}", grouped is IEnumerable<IGrouping<object, object>>);
Debug.WriteLine("IEnumerable<object>: {0}", grouped is IEnumerable<object>);
Output is as follows:
IEnumerable<IGrouping<string, KeyValuePair<int, string>>>: True
IEnumerable<IGrouping<object, KeyValuePair<int, string>>>: True
IEnumerable<IGrouping<object, KeyValuePair<object, object>>: False
IEnumerable<IGrouping<object, object>: False
IEnumerable<object>: True
So it seems that unless I know the type of the values being grouped, I can't test to see if it's grouped? Is this a bug in the .Net Framework?
How are the results above even possible?
EDIT: Jon Skeet's answer was dead on... As a courtesy to anyone else that find themselves here with the same difficulty I had, the following code did the job of checking to see if an IEnumerable was grouped or not (based off of Mr. Skeet's fancy-ass idea of using the dynamic keyword):
public static bool IsGrouped (dynamic test) {
return CheckGrouping(test);
}
static bool CheckGrouping(object o ) {
return false;
}
static bool CheckGrouping<TKey, TValue>(IEnumerable<IGrouping<TKey, TValue>> grouping) {
return true;
}
Assuming the code is the same as above:
IsGrouped(dict); //false
IsGrouped(grouped); //true
The results are not just possible but expected under .NET 4 - in .NET 3.5, the final result would be False due to the lack of generic variance.
While IGrouping<out TKey, out TValue>
is covariant in both TKey
and TValue
, KeyValuePair<Tkey, TValue>
is a struct, and therefore cannot be used in a covariant manner.
So an IGrouping<string, string>
is an IGrouping<object, object>
, but an IGrouping<string, KeyValuePair<int, string>>
is not.
This is not a bug in the framework. As for how to find out whether something implements IGrouping<TKey, TValue>
for some TKey, TValue
pair, you could either work quite hard yourself, or using dynamic
to try to help:
// Code as before...
dynamic dyn = grouped;
ShowGrouping(dyn);
private static void ShowGrouping<TKey, TValue>
(IEnumerable<IGrouping<TKey, TValue>> grouping)
{
Console.WriteLine("TKey = {0}, TValue = {1}", typeof(TKey), typeof(TValue));
}
private static void ShowGrouping(object notGrouped)
{
Console.WriteLine("Not a grouping");
}
Note that this certainly isn't foolproof - but it may be good enough for what you need.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With