.netlinqgenericstype-parametervaluetuple

Why is Enumerable.OfType<TResult>(IEnumerable) doesn't work as expected with ValueTuple?


Behavior

So I have a collection of Tuple that looks like this:

public List<(ElementRowViewModel Parent, ElementBase Element)> ResultCollection { get; private set; } = new List<(ElementRowViewModel Parent, ElementBase Element)>();

When I try to do the following

private async Task RegisterAndCommitElements<TElement>() where TElement : ElementBase
{
    var specificElements = this.ResultCollection.OfType<(ElementRowViewModel parent, TElement element)>();
}

It yields nothing. But When I do

 this.ResultCollection[0] is (ElementRowViewModel parent, SpecificElement element)

It returns true, Or even

 this.ResultCollection[0] is (ElementRowViewModel parent, TElement element)

It also returns true.

Am I miss-understanding something? Or is there a limitation or a known bug with Enumerable.OfType(IEnumerable) when using tuple as type parameter?


Solution

  • A ValueTuple<ElementRowViewModel, SpecificElement> value isn't a ValueTuple<ElementRowViewModel, ElementBase>. There's an implicit conversion from one to the other, but that's defined by the C# language (basically it performs element-wise conversion) not by the CLR type system, which is what OfType cares about.

    using System;
    
    public class Test
    {
        public static void Main(string[] args)
        {
            var strings = ("hello", "there");
            object boxed = strings;
            Console.WriteLine(boxed is ValueTuple<string, string>); // True
            Console.WriteLine(boxed is ValueTuple<object, object>); // False
    
            // This is still valid though: element-wise implicit conversions.
            (object, object) objects = strings;
        }
    }
    

    That second is test that returns false is effectively what the OfType method is doing, so it makes sense that your value isn't yielded.