0

One cool feature with C# record, is the ability to deconstruct them based on their default constructor without having to define your own Deconstruct method.

public record Foo(int Bar, int Baz);

public void Func(Foo foo) {
  var (bar, baz) = foo;
}

However, for some reason this does not seem to work when the default constructor contains only a single parameter.

public record Foo(int Bar) {
  public int? Baz { get; init; }
}

public void Func(Foo foo) {
  var (bar) = foo;
}

When trying to deconstruct a record with only one parameter in its default constructor, it produces the build errors [CS1003] Syntax error, ',' expected and [CS1001] Identifier expected. I may recall that this is also the case when defining your own Deconstruct method with only one out parameter. I am just curious as to why this is the case.

elillesaeter
  • 148
  • 10
  • I think, because deconstruction with 1 field is like assigning that field? What about `var bar = foo.Bar;` – Jeroen van Langen May 10 '22 at 12:18
  • This is not supported, the deconstruction syntax is there to make it easier to write a single statement to unpack more than one element. For just one you can just do `var bar = foo.Baz`. – Lasse V. Karlsen May 10 '22 at 12:18
  • "Deconstructing" a single value offers too much opportunity for syntactical ambiguities, and it really doesn't make anything shorter or easier (as opposed to deconstructing to multiple values) -- you can just write `var bar = foo.Bar`. Note that the type *does* provide a `Deconstruct` method even in this case, so if you *really* want you can write `foo.Deconstruct(out var bar)` to the same effect -- it's just the language that won't allow for syntactic sugaring in this case. – Jeroen Mostert May 10 '22 at 12:21
  • This is also documented [here](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct#record-types) "When you declare a record type by using **two or more** positional parameters, the compiler creates a Deconstruct method with an out parameter for each positional parameter in the record declaration" (Emphasis added) – MindSwipe May 10 '22 at 12:22
  • I can see that deconstructing a single field could be considered unnecessary. I just noticed as I have been in situations where I have made changes to my record, and simply updated the deconstruct accordingly, only to find it not working, which is a bit annoying. I realised that I guess the result of a deconstruct is a tuple, which fundamentally contains two or more elements, so it makes sense – elillesaeter May 10 '22 at 12:25
  • Besides all, having a single field in a record, sounds odd to me ;-) – Jeroen van Langen May 10 '22 at 12:26
  • @MindSwipe: interestingly, the docs are wrong there. The [language spec](https://learn.microsoft.com/dotnet/csharp/language-reference/proposals/csharp-9.0/records#deconstruct) gets it right: "A positional record with at least one parameter synthesizes a public void-returning instance method called `Deconstruct` with an `out` parameter declaration for each parameter of the primary constructor declaration". Deconstructing a single-value type isn't very useful and the language offers no syntax for it, but it's still provided for consistency. – Jeroen Mostert May 10 '22 at 12:26
  • I was also thinking whether there would be any technical difficulties such as ambiguities, which is interesting. I am aware that it would not add much to the language, just as i mentioned that I have been in situations where it would be marginally simpler to just change `var (foo, bar) = baz` to `var (foo) = baz` instead of `var foo = baz.Foo`, i.e. when making a property nullable and moving it out of the default constructor of a record – elillesaeter May 10 '22 at 12:30
  • @JeroenMostert interesting, seems like the docs and the language specs differ, maybe someone should create a issue on the relevant GH repo – MindSwipe May 10 '22 at 12:31
  • The specific syntax `var () = ` could probably be allowed as a special case without introducing ambiguity, but it would not have parity with pattern matching -- `if (foo is (var x))`, `if (foo is (var x, var y))` are different things, and changing the first to mean deconstruction could be problematic. – Jeroen Mostert May 10 '22 at 12:41

0 Answers0