BT

New Early adopter or innovator? InfoQ has been working on some new features for you. Learn more

An Early Look at C# 7.1: Part 2

| by Jonathan Allen Follow 127 Followers on Jun 06, 2017. Estimated reading time: 3 minutes |

Yesterday we looked at Async Main and Default Expressions. Our tour of C# 7.1 continues with the proposals titled Infer Tuple Names and Pattern-matching with Generics.

Infer Tuple Names

Developers don’t often think about it, but anonymous types in C# include name inference. For example, if you write the below line then the object y will have properties named A and B.

var y = new { x.A, x.B };

Under the Infer Tuple Names proposal, value tuples would have roughly the same capabilities.

var z1 = (A: x.A, B: x.B); //explicit names
var z2 = (x.A, x.B); //inferred names

There are some notable differences between anonymous types and value tuples:

  • Anonymous types require property names, either explicit or inferred.
  • Value tuples will label unnamed properties Item1, Item2, etc.
  • Anonymous types with duplicate names result in a compiler error.
  • Value tuples with duplicate explicit names result in a compiler error.
  • Value tuples with duplicate inferred names will cause name inference to the skipped. For example (x.A, x.B, y.A) becomes (Item1, B, Item3).
  • Value tuples cannot use the following reserved names: ToString, Rest, ItemN (where N > 0)

An interesting difference between C# and VB is that VB can infer anonymous property names from functions. For example:

var y = new { x.A, x.Bar() }; //compiler error
Dim y = New With {x.A, x.Bar()} //anonymous type {A,Bar}

This capability will be extended to tuples in VB.

This feature can result in a breaking change if you happen to have an extension method with the same name as an inferred property. The proposal continues,

The compatibility council found this break acceptable, given that it is limited and the time window since tuples shipped (in C# 7.0) is short.

Tuple Names in Generic Constraints

While the compiler tries to warns the programmer about tuple name mismatches. For example,

public static (int A, int B) Test1((int A, int B) a)
Test1((A: 1, B: 2));
Test1((X: 1, Y: 2)); //warning, tuple name mismatch

This doesn’t work when you start getting into generic constraints:

public static T Test2<T>(T a) where T : IEnumerable<(int A, int B)>
Test2(new List<(int A, int B)>());
Test2(new List<(int X, int Y)>()); //no warning

The current word is that tuple names in generic constraints cases simply aren’t going to be checked by the compiler. Theoretically the compiler could catch stuff like this, but the performance cost would be too great compared to the benefit.

Pattern-matching with Generics

Pattern matching, new in C# 7.0, has a design flaw when it comes to generics. Consider this code submitted by Alex Wiese:

class Program
{
    static void Main(string[] args) {}

    public void Send<T>(T packet) where T : Packet
    {
        if (packet is KeepalivePacket keepalive)
        {
            // Do stuff with keepalive
        }
        
        switch (packet)
        {
            case KeepalivePacket keepalivePacket:
                // Do stuff with keepalivePacket
                break;
        }
    }
}

public class Packet {}

public class KeepalivePacket : Packet {}

This gives us the error: “An expression of type T cannot be handled by a pattern of type KeepalivePacket.” But if we change the parameter to be of type System.Object instead of T, it works as expected.

public void Send(object packet)

In C# 7.1 this has been corrected by slightly changing the rules under which pattern matching can occur,

We change the paragraph in the pattern-matching specification (the proposed addition is shown in bold):

Certain combinations of static type of the left-hand-side and the given type are considered incompatible and result in compile-time error. A value of static type E is said to be pattern compatible with the type T if there exists an identity conversion, an implicit reference conversion, a boxing conversion, an explicit reference conversion, or an unboxing conversion from E to T, or if either E or T is an open type. It is a compile-time error if an expression of type E is not pattern compatible with the type in a type pattern that it is matched with.

This is considered to be a bug fix, but since it will “introduce a forward incompatibility” you will have to set the compiler to C# 7.1 in order to take advantage of the change.

Rate this Article

Adoption Stage
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread
Community comments

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Discuss

Login to InfoQ to interact with what matters most to you.


Recover your password...

Follow

Follow your favorite topics and editors

Quick overview of most important highlights in the industry and on the site.

Like

More signal, less noise

Build your own feed by choosing topics you want to read about and editors you want to hear from.

Notifications

Stay up-to-date

Set up your notifications and don't miss out on content that matters to you

BT