{"id":22124141,"url":"https://github.com/byme8/apparatus.aot.reflection","last_synced_at":"2026-03-03T11:39:01.484Z","repository":{"id":46996073,"uuid":"400628845","full_name":"byme8/Apparatus.AOT.Reflection","owner":"byme8","description":"Reflectionless reflection","archived":false,"fork":false,"pushed_at":"2024-06-17T13:04:02.000Z","size":142,"stargazers_count":74,"open_issues_count":4,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-28T06:27:01.451Z","etag":null,"topics":["aot-compatible","csharp","reflection","roslyn","source-generators"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/byme8.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-27T20:33:22.000Z","updated_at":"2025-07-23T07:44:27.000Z","dependencies_parsed_at":"2024-06-17T14:44:28.567Z","dependency_job_id":null,"html_url":"https://github.com/byme8/Apparatus.AOT.Reflection","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/byme8/Apparatus.AOT.Reflection","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byme8%2FApparatus.AOT.Reflection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byme8%2FApparatus.AOT.Reflection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byme8%2FApparatus.AOT.Reflection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byme8%2FApparatus.AOT.Reflection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/byme8","download_url":"https://codeload.github.com/byme8/Apparatus.AOT.Reflection/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byme8%2FApparatus.AOT.Reflection/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271345853,"owners_count":24743496,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-20T02:00:09.606Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aot-compatible","csharp","reflection","roslyn","source-generators"],"created_at":"2024-12-01T15:46:42.133Z","updated_at":"2026-03-03T11:39:01.420Z","avatar_url":"https://github.com/byme8.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AOT.Reflection is faster reflection powered via Source Generators\n\nThis library aims to create a subset of reflection that will be faster than the default one and will not break at the platforms with the AOT compilation support. The source generators will help us with that.\n\n# How to use\n\nTo make it work, you will need to install a NuGet package ``` Apparatus.AOT.Reflection ```:\n\n```\ndotnet add package Apparatus.AOT.Reflection\n```\n\nThen you can use it like that:\n\n``` cs\npublic class User\n{\n    [Required]\n    public string FirstName { get; set; }\n    [Required]\n    public string LastName { get; set; }\n}\n\npublic static void Main()\n{\n    var user = new User();\n    var properties = user.GetProperties().Values;\n    foreach (var property in properties)\n    {\n        Console.WriteLine(property.Name);\n    }\n}\n```\n\nThis sample will print the names of properties.\n```\nFirstName\nLastName\n```\n\nAlso, it works for enums too:\n``` cs \n\npublic enum UserKind \n{\n    User,\n    Admin\n}\n\n// ...\n\npublic static void Main()\n{\n    var values = EnumHelper.GetEnumInfo\u003cUserKind\u003e();\n    foreach (var value in values)\n    {\n        Console.WriteLine(value.Name);\n    }\n}\n\n```\n\nYou will see:\n```\nUser\nAdmin\n```\n\nIt does not end with the only property names. You can get property values and assigned attributes. \n\nHere is an example:\n\n``` cs\nvar requiredProperties = _user\n    .GetProperties()\n    .Values\n    .Where(o =\u003e o.Attributes.Any(attr =\u003e attr is RequiredAttribute))\n    .ToArray();\n\nforeach (var requiredProperty in requiredProperties)\n{\n    if (requiredProperty.TryGetValue(_user, out var value))\n    {\n        Console.WriteLine($\"{requiredProperty.Name} =\u003e {value}\");\n    }\n}\n```\n\nThe same applies to enums too. Let have a look at the following sample:\n``` cs \npublic enum AccountKind\n{\n    [Description(\"User account\")]\n    User,\n    [Description(\"Admin account\")]\n    Admin,\n    [Description(\"Customer account\")]\n    Customer,\n    [Description(\"Manager account\")]\n    Manager\n}\n\n// ...\n\nvar values = EnumHelper.GetEnumInfo\u003cAccountKind\u003e();\nforeach (var value in values)\n{\n    var description = value.Attributes\n        .OfType\u003cDescriptionAttribute\u003e()\n        .First();\n    \n    Console.WriteLine($\"{value.Name} =\u003e {description.Description}\");\n}\n```\n# KeyOf\n\nThe AOT.Reflection contains a way to express the intention safely when you want to pass the property inside the method. It works similarly to `` keyof `` from TypeScript. Here is an example:\n\n``` cs\nusing Apparatus.AOT.Reflection;\n\nvar user = new User {FirstName = \"Jon\", LastName = \"Smith\"};\nvar firstName = DoIt (user, \"FirstName\"); // no error\nvar lastName = DoIt (user, \"LastName\"); // no error\nvar missingProperty = DoIt (user, \"Test\"); // compilation error\n\n\nobject DoIt \u003cT\u003e (T value, KeyOf \u003cT\u003e propertyName)\n{\n     var property = value.GetProperties () [propertyName];\n     if (property.TryGetValue (value, out var propertyValue))\n     {\n         return propertyValue;\n     }\n\n     return null;\n}\n\nclass User\n{\n     public string FirstName {get; set; }\n     public string LastName {get; set; }\n}\n```\n\nMore information you can find in separate [article](https://dev.to/byme8/improving-c-with-typescript-keyof-t-1jea).\n\n# Performance\n\nLet's imagine that we need to find a property with ``` Required ``` attribute and the name  ``` FirstName ```.\nIf it exists, then print the value of the property, otherwise return the empty string. The implementation will be messy because I don't want to measure the LINQ performance, but the overall idea must be clear.\n\nHere is the source code with default reflection:\n``` cs\nvar type = _user.GetType();\nvar property = type.GetProperty(nameof(User.FirstName));\n\nvar required = false;\nforeach (var o in property.GetCustomAttributes())\n{\n    if (o.GetType() == typeof(RequiredAttribute))\n    {\n        required = true;\n        break;\n    }\n}\n\nif (required)\n{\n    return (string)property.GetMethod?.Invoke(_user, null);\n}\n\nreturn string.Empty;\n\n```\n\nHere the source code with aot reflection:\n``` cs \nvar entries = _user.GetProperties();\nvar firstName = entries[nameof(User.FirstName)];\n\nvar required = false;\nforeach (var o in firstName.Attributes)\n{\n    if (o is RequiredAttribute)\n    {\n        required = true;\n        break;\n    }\n}\n\nif (required)\n{\n    if (firstName.TryGetValue(_user, out var value))\n    {\n        return (string)value;\n    }\n\n    return string.Empty;\n}\n\nreturn string.Empty;\n```\n\nHere are the benchmark results:\n``` \nBenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1165 (21H1/May2021Update)\n11th Gen Intel Core i7-11700KF 3.60GHz, 1 CPU, 16 logical and 8 physical cores\n.NET SDK=6.0.100-preview.7.21379.14\n  [Host]     : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT\n  DefaultJob : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT\n\n\n|        Method |        Mean |    Error |   StdDev |  Gen 0 | Allocated |\n|-------------- |------------:|---------:|---------:|-------:|----------:|\n|    Reflection | 1,758.91 ns | 2.714 ns | 2.406 ns | 0.1278 |   1,072 B |\n| AOTReflection |    16.01 ns | 0.090 ns | 0.075 ns |      - |         - |\n```\n\nAs you can see, the AOT.Reflection is significantly faster comparing to default reflection.\n\nNow let's have a look at enums performance. Imagine that we have the enum value, and we need to get a description associated with it.\nHere how it will look:\n``` cs \nvar attributes = _account.GetEnumValueInfo().Attributes;\nfor (int i = 0; i \u003c attributes.Length; i++)\n{\n    var attribute = attributes[i];\n    if (attribute is DescriptionAttribute descriptionAttribute)\n    {\n        return descriptionAttribute.Description;\n    }\n}\n\nreturn \"\";\n```\n\nHere is the results:\n``` cs\n|              Method |       Mean |     Error |    StdDev |  Gen 0 | Allocated |\n|-------------------- |-----------:|----------:|----------:|-------:|----------:|\n|        GetValuesAOT |   6.253 ns | 0.0394 ns | 0.0329 ns |      - |         - |\n| GetValuesReflection | 734.563 ns | 2.3173 ns | 1.9351 ns | 0.0324 |     272 B |\n```\nAnd again, the AOT reflection works much faster.\n\nThe complete source code of benchmarks you can find [here](https://github.com/byme8/Apparatus.AOT.Reflection/blob/master/src/Apparatus.AOT.Reflection.Benchmark/Program.cs).\n\n# Limitations\n\nI would recommend being careful when you try to use these APIs inside the generic methods because, at this point, there is no easy way to analyze them and identify the correct signatures. It means the source generation will not happen. As a result, we will have an error at runtime.\nLet's have a look at the following sample:\n``` cs\npublic class Program\n{\n    public static string? GetDescription\u003cT\u003e(T enumValue)\n        where T : Enum\n    {\n        return enumValue\n            .GetEnumValueInfo()\n            .Attributes\n            .OfType\u003cDescriptionAttribute\u003e()\n            .FirstOrDefault()\n            ?.Description;\n    }\n    \n    public static void Main()\n    {\n        var account = AccountKind.Admin;\n        Console.WriteLine(GetDescription(account));\n    }\n}\n```\nWe will have an exception if we run it because the source generator could not figure out the signatures. The type `` T `` is the mystery for it.\nBut we can fix it with a small trick:\n``` cs\npublic class Program\n{\n    private void DontCallMe()\n    {\n        EnumHelper.GetEnumInfo\u003cAccountKind\u003e();\n    }\n    \n    public static string? GetDescription\u003cT\u003e(T enumValue)\n        where T : Enum\n    {\n        return enumValue\n            .GetEnumValueInfo()\n            .Attributes\n            .OfType\u003cDescriptionAttribute\u003e()\n            .FirstOrDefault()\n            ?.Description;\n    }\n    \n    public static void Main()\n    {\n        var account = AccountKind.Admin;\n        Console.WriteLine(GetDescription(account));\n    }\n}\n\n```\nPay attention to the ``DontCallMe `` method. We do not have any intention to use it anywhere. It is here to help the source generator to analyze the source code. Now, if we run it, everything works as expected.\nThe same issue exists for the properties reflection, and we can use the same trick to avoid it.\n\n\n# Support\n\nRight now, only public properties and enums are supported. Regarding the private members, I doubt them because they would ruin the performance, but we will see.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyme8%2Fapparatus.aot.reflection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbyme8%2Fapparatus.aot.reflection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyme8%2Fapparatus.aot.reflection/lists"}