Skip to content

Commit

Permalink
#7 updated relevant documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonBock committed Nov 25, 2022
1 parent 8263c42 commit b1e73c8
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ A library that automatically adds support for object deconstruction in C#.

The idea started with [this tweet](https://twitter.com/buhakmeh/status/1462106117564207104) - specifically, [this reply](https://twitter.com/dave_peixoto/status/1462181358248374278). I thought...how automatic can I make object deconstruction in C#? That's what this source generator is all about.

Read [the design document](docs/Design.md) for further details.
Read [the overview document](docs/Overview.md) for further details.
5 changes: 4 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - Not Released Yest
## [1.0.0] - 2022.11.25

### Added
- Generated code now uses fully-qualified named (issue [#5](https://github.com/JasonBock/AutoDeconstruct/issues/5))
- Records are now considered for `Deconstruct()` generation (issue [#4](https://github.com/JasonBock/AutoDeconstruct/issues/4))
- Putting `[NoAutoDeconstruct]` on a type will signal AutoDeconstruct not to generate a `Deconstruct()` method (issue [#6](https://github.com/JasonBock/AutoDeconstruct/issues/6))
- All parameter names now have `@` in front to eliminate any potential keyword conflicts (issue [#9](https://github.com/JasonBock/AutoDeconstruct/issues/9))

### Changed
- Updated documentation (issue [#7](https://github.com/JasonBock/AutoDeconstruct/issues/7))
- Generated code is now stored in one file (issue [#2](https://github.com/JasonBock/AutoDeconstruct/issues/2))

### Fixed
Expand Down
133 changes: 133 additions & 0 deletions docs/Overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Table of Contents
- [What is a Deconstructor?](#what-is-a-deconstructor?)
- [AutoDeconstruct Features](#autodeconstruct-rationale)

## What is a Deconstructor?

Object deconstruction was added in C# 7.0. The documentation is [here](https://github.com/dotnet/roslyn/blob/main/docs/features/deconstruction.md), and there's another article [here](https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct#user-defined-types). Basically, a deconstructor can be defined on either a type or as an extension method. In both cases, it has to be named "Deconstruct", it has to return `void`, and all of its parameters must be `out` parameters (the exception is with the extension method, where the first parameter is the object being extended). Furthermore, you can overload `Deconstruct` methods, but all `Deconstruct` methods must have a unique number of `out` parameters. Here are two examples:

```csharp
using System;

public sealed class Customer
{
public Customer(Guid id, string name) =>
(this.Id, this.Name) = (id, name);

public void Deconstruct(out Guid id, out string name) =>
(id, name) = (this.Id, this.Name);

public Guid Id { get; }
public string Name { get; }
}

var customer = new Customer(Guid.NewGuid(), "Jason");
var (id, name) = customer;

public struct Point
{
public Point(int x, int y) =>
(this.X, this.Y) = (x, y);

public int X { get; }
public int Y { get; }
}

public static class PointExtensions
{
public static void Deconstruct(this Point self, out int x, out int y) =>
(x, y) = (self.X, self.Y);
}

var point = new Point(2, 3);
var (x, y) = point;
```

## AutoDeconstruct Features

AutoDeconstruct finds all type and method definitions, and it'll look to see if the type has any `Deconstruct` methods, either as instance or extension methods. If none exist, then AutoDeconstruct looks to see how many public, readable, instance properties exist. If there's at least 1, the library generates a `Deconstruct` extension method in a static class defined in the same namespace as the target type. For example, if we have our `Point` type defined like this:

```csharp
namespace Maths.Geometry;

public struct Point
{
public Point(int x, int y) =>
(this.X, this.Y) = (x, y);

public int X { get; }
public int Y { get; }
}
```

Then the library generates this:

```csharp
#nullable enable

namespace Maths.Geometry
{
public static partial class PointExtensions
{
public static void Deconstruct(this global::Maths.Geometry.Point @self, out int @x, out int @y) =>
(@x, @y) = (@self.X, @self.Y);
}
}
```

If the target type is a reference type, a null check will be generated. Furthermore, the `Deconstruct` extension method will also be created if a `Deconstruct` doesn't exist with the number of properties found. For example, let's say we have this:

```csharp
namespace Models;

public sealed class Person
{
public void Deconstruct(out Guid id) =>
id = this.Id;

public uint Age { get; init; }
public Guid Id { get; init; }
public string Name { get; init; }
}

public static class PersonExtensions
{
public static void Deconstruct(this Person self, out Guid id, out string name) =>
(id, name) = (self.Id, self.Name);
}
```

AutoDeconstruct would see that there are three properties that could be used for a generated `Deconstruct`. The two `Deconstruct` methods that exist have one and two `out` parameters, so it will generate one that has all three properties as `out` parameters:

```csharp
#nullable enable

namespace Models
{
public static partial class PersonExtensions
{
public static void Deconstruct(this global::Models.Person @self, out global::System.Guid @id, out string @name, out uint @age)
{
global::System.ArgumentNullException.ThrowIfNull(@self);
(@id, @name, @age) = (@self.Id, @self.Name, @self.Age);
}
}
}
```

While AutoDeconstruct will do a complete search for types to generate `Deconstruct` methods, a user may want to opt out of this search for specific types. Adding the `[NoAutoDeconstruct]` attribute to a type will tell AutoDeconstrct to ignore it:

```csharp
namespace AutoDeconstruct;
namespace Models;

[NoAutoDeconstruct]
public sealed class Person
{
public uint Age { get; init; }
public Guid Id { get; init; }
public string Name { get; init; }
}
```

In this case, AutoDeconstruct will not generate a `Deconstruct` method for `Person`.
File renamed without changes.

0 comments on commit b1e73c8

Please sign in to comment.