Skip to content

Commit 00fcb3d

Browse files
committed
#16 updated docs, added icon, updated changelog, time to merge
1 parent 1001fa8 commit 00fcb3d

File tree

6 files changed

+126
-20
lines changed

6 files changed

+126
-20
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22

33
A library that automatically adds support for object deconstruction in C#.
44

5+
## Getting Started
6+
7+
First, reference the `AutoDeconstruct` [NuGet package](https://www.nuget.org/packages/AutoDeconstruct).
8+
9+
Then, add `[AutoDeconstuct]` to a type so you can deconstruct it:
10+
11+
```c#
12+
using AutoDeconstruct;
13+
14+
[AutoDeconstruct]
15+
public sealed class Person
16+
{
17+
public uint Age { get; set; }
18+
public required string Name { get; set; }
19+
}
20+
21+
var person = new Person { Age = 22, Name = "Joe" };
22+
var (age, name) = person;
23+
```
24+
25+
Read [the overview document](docs/Overview.md) for further details.
26+
27+
### Prerequisites
28+
29+
The Rocks package targets .NET Standard 2.0 for host flexibility.
30+
531
## Overview
632

733
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.

changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.1.0] - 2025.02.01
9+
10+
### Added
11+
- AutoDeconstruct no longer automatically scans for types; `[AutoDeconstruct]` must be used (this improves source generator performance) (issue [#16](https://github.com/JasonBock/AutoDeconstruct/issues/16))
12+
813
## [1.0.0] - 2022.11.25
914

1015
### Added

docs/Overview.md

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
# Table of Contents
2-
- [What is a Deconstructor?](#what-is-a-deconstructor?)
3-
- [AutoDeconstruct Features](#autodeconstruct-rationale)
2+
- [Motivation](#motivation)
3+
- [What is a Deconstructor?](#what-is-a-deconstructor)
4+
- [AutoDeconstruct Features](#autodeconstruct-features)
5+
- [Marking Types](#marking-types)
6+
- [Assembly-Level Support](#assembly-level-support)
7+
- [Ignore Deconstruction Creation](#ignore-deconstruction-creation)
8+
9+
## Motivation
10+
11+
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.
412

513
## What is a Deconstructor?
614

7-
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:
15+
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 type being extended). Furthermore, `Deconstruct()` methods can overloaded, but all `Deconstruct()` methods must have a unique number of `out` parameters. Here are two examples:
816

917
```csharp
1018
using System;
@@ -43,13 +51,17 @@ var point = new Point(2, 3);
4351
var (x, y) = point;
4452
```
4553

54+
Note that what values are deconstructed is up to the developer. That is, deconstruction does not require one to deconstruct all property or field values.
55+
4656
## AutoDeconstruct Features
4757

48-
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:
58+
### Marking Types
59+
AutoDeconstruct looks to see if the target type has any `Deconstruct()` methods, either as instance or extension methods (if extension methods are searched for - this is discussed later in this document). If none exist, then AutoDeconstruct looks to see how many accessible, 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:
4960

5061
```csharp
5162
namespace Maths.Geometry;
5263

64+
[AutoDeconstruct]
5365
public struct Point
5466
{
5567
public Point(int x, int y) =>
@@ -75,29 +87,26 @@ namespace Maths.Geometry
7587
}
7688
```
7789

78-
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:
90+
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:
7991

8092
```csharp
93+
using AutoDeconstruct;
94+
8195
namespace Models;
8296

97+
[AutoDeconstruct]
8398
public sealed class Person
8499
{
85-
public void Deconstruct(out Guid id) =>
86-
id = this.Id;
87-
88100
public uint Age { get; init; }
89101
public Guid Id { get; init; }
90102
public string Name { get; init; }
91-
}
92103

93-
public static class PersonExtensions
94-
{
95-
public static void Deconstruct(this Person self, out Guid id, out string name) =>
96-
(id, name) = (self.Id, self.Name);
104+
public void Deconstruct(out Guid id) =>
105+
id = this.Id;
97106
}
98107
```
99108

100-
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:
109+
AutoDeconstruct would see that there are three properties that could be used for a generated `Deconstruct()`. The `Deconstruct()` method that exists has one `out` parameter, so it will generate one that has all three properties as `out` parameters:
101110

102111
```csharp
103112
#nullable enable
@@ -115,19 +124,82 @@ namespace Models
115124
}
116125
```
117126

118-
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:
127+
### Assembly-Level Support
128+
129+
`[AutoDeconstruct]` can also be defined at the assembly level to inform AutoDeconstruct to add `Deconstruct()` extension methods for **every** type in the assembly:
130+
131+
```csharp
132+
using AutoDeconstruct;
133+
134+
[assembly: AutoDeconstruct]
135+
136+
namespace Models;
137+
138+
public sealed class Person
139+
{
140+
public uint Age { get; init; }
141+
public Guid Id { get; init; }
142+
public string Name { get; init; }
143+
144+
public void Deconstruct(out Guid id) =>
145+
id = this.Id;
146+
}
147+
```
148+
149+
While AutoDeconstruct will search the target type to see if an existing `Deconstruct()` method exists that matches what AutoDeconstruct would do, a user may want to opt int to a search to look through the entire assembly for `Deconstruct()` extension methods. By default, this is turned off as it's not common to create deconstruction methods this way, but if this completeness is desired, `SearchForExtensionMethods.Yes` can be passed into `[AutoDeconstruct]`:
150+
151+
```csharp
152+
using AutoDeconstruct;
153+
154+
[assembly: AutoDeconstruct(SearchForExtensionMethods.Yes)]
155+
156+
namespace Models;
157+
158+
public sealed class Person
159+
{
160+
public uint Age { get; init; }
161+
public Guid Id { get; init; }
162+
public string Name { get; init; }
163+
164+
public void Deconstruct(out Guid id) =>
165+
id = this.Id;
166+
}
167+
168+
public static partial class PersonExtensions
169+
{
170+
public static void Deconstruct(this Person @self, out Guid @id, out string @name, out uint @age) =>
171+
(@id, @name, @age) = (@self.Id, @self.Name, @self.Age);
172+
}
173+
```
174+
175+
In this case, AutoDeconstruct will detect an existing `Deconstruct()` method that already does what AutoDeconstruct would generate, so no code is generated. This search flag also works when `[AutoDeconstruct]` is defined on a specific type.
176+
177+
### Ignore Deconstruction Creation
178+
179+
The `[NoAutoDeconstruct]` attribute can be added to a type will tell AutoDeconstrct to ignore it. Note that this is only relevant when `[AutoDeconstruct]` is added at the assembly level:
119180

120181
```csharp
121182
namespace AutoDeconstruct;
122183
namespace Models;
123184

185+
[assembly: AutoDeconstruct]
186+
124187
[NoAutoDeconstruct]
125188
public sealed class Person
126189
{
127190
public uint Age { get; init; }
128191
public Guid Id { get; init; }
129192
public string Name { get; init; }
130193
}
194+
195+
public struct Point
196+
{
197+
public Point(int x, int y) =>
198+
(this.X, this.Y) = (x, y);
199+
200+
public int X { get; }
201+
public int Y { get; }
202+
}
131203
```
132204

133-
In this case, AutoDeconstruct will not generate a `Deconstruct` method for `Person`.
205+
In this case, AutoDeconstruct will not generate a `Deconstruct` method for `Person`, but it will for `Point`. If a type has `[AutoDeconstruct]` and `[NoAutoDeconstruct]`, `[AutoDeconstruct]` "wins".
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
* Update docs
1+
* DONE - Update docs
22
* DONE - I added `internal` properties to the list, as it's possible someone may want to deconstruct internally. But, now I need to make the static class and the extension method `internal` if either the target type is `internal`, or any of the properties are `internal`.
33
* DONE - Finding matching `Deconstruct()` extension methods isn't correct. We need to also look at the parameter types.
44
* DONE - I think I overdid it. If the `Deconstruct()` has the same number of property as parameters, then it has to be excluded, because deconstruct methods can't be overloaded with the same number of parmaters.
@@ -8,7 +8,7 @@
88
* DONE - Put a test in to show that a `Deconstruct()` is made even if both attributes exist on the type.
99
* DONE - Consider making the generated extension method part of the type declaration if the type is `partial`. See this for details: https://stackoverflow.com/questions/68906372/roslyn-analyzer-is-class-marked-as-partial
1010
* DONE - Add code to change the name of extension type if there is a collision
11-
* Add a flag to `[AutoDeconstruct]` to only do a full assembly search of extension method, something like `[AutoDeconstruct(SearchForExtensionMethods.Yes)]`. It would be `SearchForExtensionMethods.No` by default, you need to opt-in to do the search. Most of the time, the search is unnecessary.
11+
* DONE - Add a flag to `[AutoDeconstruct]` to only do a full assembly search of extension method, something like `[AutoDeconstruct(SearchForExtensionMethods.Yes)]`. It would be `SearchForExtensionMethods.No` by default, you need to opt-in to do the search. Most of the time, the search is unnecessary.
1212

1313

1414
Also...https://discord.com/channels/143867839282020352/598678594750775301/1068691525615239168

src/AutoDeconstruct/AutoDeconstruct.csproj

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<AssemblyName>AutoDeconstruct</AssemblyName>
44
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
@@ -9,6 +9,7 @@
99
<GenerateDocumentationFile>true</GenerateDocumentationFile>
1010
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1111
<IncludeSymbols>true</IncludeSymbols>
12+
<PackageIcon>Icon.png</PackageIcon>
1213
<PackageId>AutoDeconstruct</PackageId>
1314
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1415
<PackageProjectUrl>https://github.com/jasonbock/autodeconstruct</PackageProjectUrl>
@@ -17,17 +18,19 @@
1718
<PackageTags>deconstruct;source generator</PackageTags>
1819
<PackageVersion>$(Version)</PackageVersion>
1920
<PublishRepositoryUrl>true</PublishRepositoryUrl>
21+
<RepositoryType>git</RepositoryType>
2022
<RepositoryUrl>https://github.com/jasonbock/autodeconstruct</RepositoryUrl>
2123
<RootNamespace>AutoDeconstruct</RootNamespace>
2224
<TargetFramework>netstandard2.0</TargetFramework>
2325
</PropertyGroup>
2426
<ItemGroup>
2527
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" PrivateAssets="all" />
26-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
28+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" />
2729
</ItemGroup>
2830
<ItemGroup>
2931
<None Include="..\..\README.md" Pack="true" PackagePath="" />
3032
<None Include="..\..\LICENSE" Pack="true" PackagePath="" />
33+
<None Include="..\Images\Icon.png" Pack="true" PackagePath="" Visible="false" />
3134
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
3235
</ItemGroup>
3336
</Project>

src/Images/Icon.png

3.67 KB
Loading

0 commit comments

Comments
 (0)