en-US
TypeHarvester is an incremental Roslyn source generator that scans assemblies for types marked with specified attributes and generates a strongly-typed cache class named TypesByAttributes.
This cache provides several overloads of the Get method, allowing you to retrieve all types annotated with one or more attributes without using reflection at runtime.
TypeHarvester exists to eliminate reflection-based type discovery and the numerous issues that come with it:
- The target assembly may not be loaded into the
AppDomainwhen reflection runs. - Version conflicts or isolation between different
AssemblyLoadContexts. - Assemblies missing from
AppDomain.CurrentDomain.GetAssemblies()enumeration. ReflectionTypeLoadExceptiondue to inaccessible types.- Problems with dynamic or reflection-only assemblies.
- Attributes inaccessible due to missing dependency assemblies.
- Types trimmed by the .NET linker (Trimming in .NET Core+).
By collecting metadata at compile time, TypeHarvester prevents these leaky abstractions of reflection and provides a fast, reliable, compile-time cache of attributed types.
In your project’s .csproj:
<ItemGroup>
<PackageReference Include="TypeHarvester" Version="latest">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets> analyzers;</IncludeAssets>
</PackageReference>
<AdditionalFiles Include="codegen.config.json" />
</ItemGroup>Then, place a codegen.config.json file in your project root:
{
"CollectTypesWithAttributes": {
"Attributes": [
"TypeHarvester.Debug.Dependency.MyAttribute"
],
"NamespaceForGenerations": "TypeHarvester.Debug",
"CollectToCache": false,
"Partial": false
}
}| Setting | Description |
|---|---|
Attributes |
List of attribute types to collect. |
NamespaceForGenerations |
Namespace for generated code. Must match your stub file’s namespace. If omitted, code is generated in the global namespace (not recommended). |
CollectToCache |
If true, collected data is stored in assembly metadata instead of generating code. Used for dependent projects. |
Partial |
If true, generated class and methods are marked as partial to support IntelliSense fixes. |
- For dependent projects (where you just collect attributed types):
Set"CollectToCache": trueand omitNamespaceForGenerations. - For the top-level project (where code is generated):
Set"CollectToCache": falseand specifyNamespaceForGenerations.
This way, TypeHarvester collects attributed types from all projects and consolidates them into a single generated class in the top-level project.
If IntelliSense reports that the generated class does not exist (but build succeeds), apply this workaround:
- In
codegen.config.json, set"Partial": true. - Add a stub file manually:
namespace TypeHarvester.Debug; using System; using System.Collections.Generic; internal static partial class TypesByAttributes { internal static partial IEnumerable<Type> Get<TAttribute>(); internal static partial IEnumerable<Type> Get<TAttribute1, TAttribute2>(); internal static partial IEnumerable<Type> Get<TAttribute1, TAttribute2, TAttribute3>(); internal static partial IEnumerable<Type> Get(params Type[] attributeTypes); }
ru-RU
TypeHarvester — это инкрементальный Roslyn-генератор исходного кода, который находит все типы, помеченные указанными атрибутами, и генерирует класс-кэш TypesByAttributes.
Этот класс содержит несколько перегрузок метода Get, позволяющих получать все типы, аннотированные одним или несколькими атрибутами, без использования рефлексии во время выполнения.
Проект создан для того, чтобы избавиться от использования рефлексии для поиска типов и всех сопутствующих проблем:
- Сборка с нужным типом может не быть загружена в
AppDomainв момент вызова. - Конфликты версий сборок или изоляция в разных
AssemblyLoadContext. - Отсутствие нужных сборок в
AppDomain.CurrentDomain.GetAssemblies(). - Ошибка
ReflectionTypeLoadExceptionпри частичной недоступности типов. - Проблемы с динамическими или reflection-only сборками.
- Недоступность атрибутов из-за не загруженных зависимых сборок.
- Удаление типов оптимизатором (Trimming) в .NET Core+.
TypeHarvester решает проблему протекающих абстракций рефлексии, собирая данные на этапе компиляции и создавая быстрый и надёжный кэш типов.
В .csproj проекта:
<ItemGroup>
<PackageReference Include="TypeHarvester" Version="latest">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets> analyzers;</IncludeAssets>
</PackageReference>
<AdditionalFiles Include="codegen.config.json" />
</ItemGroup>Создайте в корне проекта файл codegen.config.json:
{
"CollectTypesWithAttributes": {
"Attributes": [
"TypeHarvester.Debug.Dependency.MyAttribute"
],
"NamespaceForGenerations": "TypeHarvester.Debug",
"CollectToCache": false,
"Partial": false
}
}| Параметр | Описание |
|---|---|
Attributes |
Список атрибутов, по которым будут собираться типы. |
NamespaceForGenerations |
Пространство имён для сгенерированного кода. Должно совпадать с пространством имён stub-файла. Если отсутствует — генерация идёт в глобальный неймспейс (не рекомендуется). |
CollectToCache |
Если true, данные сохраняются в метаданные сборки, а не генерируется код. Используется в зависимых проектах. |
Partial |
Если true, код будет сгенерирован как partial для устранения ошибок IntelliSense. |
- В зависимых проектах:
"CollectToCache": true, параметрNamespaceForGenerationsможно опустить. - В основном (топ-левел) проекте:
"CollectToCache": falseи обязательно указатьNamespaceForGenerations.
Таким образом, генератор соберёт типы из всех подключённых проектов и создаст единый класс-кэш в основном проекте.
Если IntelliSense жалуется на отсутствие класса (при этом сборка успешно компилируется):
- Установите
"Partial": trueв конфиге. - Добавьте stub-файл:
namespace TypeHarvester.Debug; using System; using System.Collections.Generic; internal static partial class TypesByAttributes { internal static partial IEnumerable<Type> Get<TAttribute>(); internal static partial IEnumerable<Type> Get<TAttribute1, TAttribute2>(); internal static partial IEnumerable<Type> Get<TAttribute1, TAttribute2, TAttribute3>(); internal static partial IEnumerable<Type> Get(params Type[] attributeTypes); }