You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add cookbook section for avoiding inheritance (dotnet#79276)
* Add cookbook section for avoiding inheritance
Adds a section to the incremental generators cookbook on why users should avoid inheritance, that goes over a few possible scenarios and how they can hurt IDE responsiveness.
* Update ToC
* Add a small note on preferring FAWMN.
-[Use an indented text writer, not `SyntaxNode`s, for generation](#use-an-indented-text-writer-not-syntaxnodes-for-generation)
24
+
-[Put `Microsoft.CodeAnalysis.EmbeddedAttribute` on generated marker types](#put-microsoftcodeanalysisembeddedattribute-on-generated-marker-types)
25
+
-[Do not scan for types that indirectly implement interfaces, indirectly inherit from types, or are indirectly marked by an attribute from an interface or base type](#do-not-scan-for-types-that-indirectly-implement-interfaces-indirectly-inherit-from-types-or-are-indirectly-marked-by-an-attribute-from-an-interface-or-base-type)
@@ -135,6 +137,27 @@ project, it will not include that type in lookup results. To ensure that `Micros
135
137
Another option is to provide an assembly in your nuget package that defines your marker attributes, but this can be more difficult to author. We recommend the
136
138
`EmbeddedAttribute` approach, unless you need to support versions of Roslyn lower than 4.14.
137
139
140
+
### Do not scan for types that indirectly implement interfaces, indirectly inherit from types, or are indirectly marked by an attribute from an interface or base type
141
+
142
+
Using an interface/base type marker can be a very tempting and natural fit for generators. However, scanning for these types of markers is _very_ expensive, and cannot
143
+
be done incrementally. Doing so can have an outsized impact on IDE and command-line performance, even for fairly small consuming users. These scenarios are:
144
+
145
+
* A user implements an interface on `BaseModelType`, and then the generator looks all derived types from `BaseModelType`. Because the generator cannot know ahead of time
146
+
what `BaseModelType` actually is, it means that the generator has to fetch `AllInterfaces` on every single type in the compilation so it can scan for the marker
147
+
interface. This will end up occurring either on every keystroke or every file save, depending on what mode the user is running generators in; either one is disastrous
148
+
for IDE performance, even when trying to optimize by scoping down the scanning to only types with a base list.
149
+
* A user inherits from a generator-defined `BaseSerializerType`, and the generator looks for anything that inherits from that type, either directly or indirectly. Similar
150
+
to the above scenario, the generator will need to scan all types with a base type in the entire compilation for the inherited `BaseSerializerType`, which will heavily
151
+
impact IDE performance.
152
+
* A generator looks among all base types/implemented interfaces for a type that is attributed with a generator's marker attribute. This is effectively either scenario 1
153
+
or 2, just with a different search criteria.
154
+
* A generator leaves its marker attribute unsealed, and expects users to be able to derive their own attributes from that marker, as a source of parameter customization.
155
+
This has a couple of problems: first, every attributed type needs to be checked to see if the attribute inherits from the marker attribute. While not as performance
156
+
impacting as the first three scenarios, this isn't great for performance. Second, and more importantly, there is no good way to retrieve any customizations from the
157
+
inherited attribute. These attributes are not instantiated by the source generator, so any parameters passed to the `base()` constructor call or values that are assigned
158
+
to any properties of the base attribute are not visible to the generator. Prefer using FAWMN-driven development here, and using an analyzer to inform the user if they
159
+
need to inherit from some base class for your generator to work correctly.
160
+
138
161
## Designs
139
162
140
163
This section is broken down by user scenarios, with general solutions listed first, and more specific examples later on.
0 commit comments