Reputation: 19937
Using .NET 6
and C# 10
. Consider this simple type-check:
(object, bool) untyped = new("", false);
if (untyped is (string, bool) typed)
{
Console.WriteLine(typed.GetType());
}
And the output:
System.ValueTuple`2[System.Object,System.Boolean]
So I managed to have the cookie and eat it at the same time. Apparently, the typed
variable is of type (string, bool)
and (object, bool)
.
I'm trying to wrap my head around this. It's tempting to state that the "compiler is broken" but maybe I've misunderstood some C#
fundamentals.
The IL
speaks for itself.
Please explain the ambiguity of this type-check! Is the pattern matching operator broken?
Upvotes: 4
Views: 132
Reputation: 42225
I think that's happening is that the logic is the same as:
if (c is (string, bool) d)
{
// 'd' is a instance of 'C', not a tuple
}
public class C
{
public void Deconstruct(out object o, out bool b) => (o, b) = ("", false);
}
Which is probably more useful as e.g.:
if (c.D is (string, bool) and { Length: >0 } d)
{
// 'd' is an instance of 'D'
}
public class C
{
public D D { get; set; } = new();
}
public class D
{
public int Length { get; set; }
public void Deconstruct(out object o, out bool b) => (o, b) = ("", false);
}
That is, the is (...)
pattern is a positional pattern and not a type pattern. In a positional pattern, we're deconstructing the object and applying patterns to each of its parts, but the designation d
applies to the object we're applying the positional pattern to, and not to a tuple constructed of its parts.
I agree this is highly confusing when we're just matching against a tuple as in your question. It does make a bit more sense in my second example above however, where c.D
might be a complex expression and we want the ability to bind it to a variable while also applying patterns to it.
The actual spec doesn't seem to cover this case: it doesn't specify what type simple_designation
is or what it's bound to.
Upvotes: 1