{"id":26766198,"url":"https://github.com/sourcegeneration/reflection","last_synced_at":"2025-04-15T12:36:04.091Z","repository":{"id":222597138,"uuid":"757245047","full_name":"SourceGeneration/Reflection","owner":"SourceGeneration","description":"Reflection is a source-generated reflection library, with global caching and support for AOT compilation","archived":false,"fork":false,"pushed_at":"2025-04-10T08:58:03.000Z","size":278,"stargazers_count":21,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T09:43:25.452Z","etag":null,"topics":["aot","constructor","createinstance","csharp","dotnet","methodinfo","propertyinfo","reflection","reflection-library","serializer","sourcegeneration","sourcegenerator"],"latest_commit_sha":null,"homepage":"https://github.com/SourceGeneration/Reflection","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/SourceGeneration.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":"2024-02-14T04:57:26.000Z","updated_at":"2025-04-10T08:58:07.000Z","dependencies_parsed_at":"2024-02-25T06:57:36.198Z","dependency_job_id":"2f5c7346-630a-48d7-b6f3-2da90d4b49af","html_url":"https://github.com/SourceGeneration/Reflection","commit_stats":null,"previous_names":["sourcegeneration/reflection"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SourceGeneration%2FReflection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SourceGeneration%2FReflection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SourceGeneration%2FReflection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SourceGeneration%2FReflection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SourceGeneration","download_url":"https://codeload.github.com/SourceGeneration/Reflection/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249072825,"owners_count":21208253,"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","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","constructor","createinstance","csharp","dotnet","methodinfo","propertyinfo","reflection","reflection-library","serializer","sourcegeneration","sourcegenerator"],"created_at":"2025-03-28T20:19:28.031Z","updated_at":"2025-04-15T12:36:04.084Z","avatar_url":"https://github.com/SourceGeneration.png","language":"C#","readme":"\n\n# Reflection\n\n[![NuGet](https://img.shields.io/nuget/vpre/SourceGeneration.Reflection.svg)](https://www.nuget.org/packages/SourceGeneration.Reflection)\n\n## Why\n\nWith the development of .NET, there is an increasing need for `AOT Native` in many applications. However, reflection and dynamic code pose obstacles to `AOT` deployment. `Source generators` can effectively this issue. For example, `System.Json.Text` use `SourceGenerator` to handle object serialization. However, these implementations are specific to individual businesses and cannot be easily generalized.\n\nSourceReflection aims to provide a more universal solution, offering `AOTable` Reflection support to more developers without the need for repetitive source generator implementation.\n\n## Supports\n\n**Supports the following types**\n- Class\n- Record\n- Struct (ref struct is not supports)\n- Enum\n- Array\n- GenericType(just handle known types)\n\n**Supports the the following members**\n- Field\n- Property\n- Indexer\n- Method\n- Constructor\n\n**Unsupports**\n- Generic type definition\n- Generic method *\n\n\u003e Currently, support for generic type definition is not yet available. The main issue lies in the handling of `MakeGenericType`.\n\n\u003e Similar to generic types, there are also generic methods. The issue lies in `MakeGenericMethod`. However, it can currently handle some specific cases, such as situations where the generic type can be inferred.\n\n`.NET9.0` has made significant improvements to AOT and can support `MakeGenericType` and `MakeGenericMethod`. However, there is still a significant amount of work to be done in SourceReflection. Support will be added in subsequent versions\n\n**Adapters**\n- `System.Text.Json` Adapter, supports `AOT` without JsonSerializerContext\n  \n## Installing Reflection\n\n```powershell\nInstall-Package SourceGeneration.Reflection -Version 1.0.0-beta2.250415.0\n```\n```powershell\ndotnet add package SourceGeneration.Reflection --version 1.0.0-beta2.250415.0\n```\n\n## Source Reflection\n\nSourceReflection requires using an attribute to mark the type\n- Adding `[SourceReflectionAttribute]` to type.\n- Adding `SourceReflectionTypeAttribute]` to assembly, it is possible to specify types from other assemblies\n\n```c#\n[assembly: SourceReflectionType\u003cobject\u003e]\n[assembly: SourceReflectionType(typeof(Enumerable))]\n\n[SourceReflection]\npublic class Goods { }\n```\n\n## Basic\n\nAdd `[SourceReflectionAttribute]` to your class\n```c#\nusing SourceGeneration.Reflection;\n\n[SourceReflection]\npublic class Goods\n{\n    private int Id { get; set; }\n    public string Name { get; private set; }\n    public double Price { get; set; }\n\n    internal void Discount(double discount)\n    {\n        Price = Price * discount;\n    }\n}\n```\n\n```c#\nusing SourceGeneration.Reflection;\n\n// Get TypeInfo\nvar type = SourceReflector.GetType(typeof(Goods));\n\n// Get default ConstructorInfo and create a instance\nvar goods = (Goods)type.GetConstructor([]).Invoke([]);\n\n// Get PropertyInfo and set value\ntype.GetProperty(\"Id\").SetValue(goods, 1); // private property\ntype.GetProperty(\"Name\").SetValue(goods, \"book\"); // private property setter\ntype.GetProperty(\"Price\").SetValue(goods, 3.14); // public property\n\n// Output book\nConsole.WriteLine(goods.Name);\n// Output 1\nConsole.WriteLine(type.GetProperty(\"Id\").GetValue(goods));\n// Output 3.14\nConsole.WriteLine(goods.Price);\n\n// Get MethodInfo and invoke\ntype.GetMethod(\"Discount\").Invoke(goods, [0.5]);\n// Output 1.57\nConsole.WriteLine(goods.Price);\n```\n\n## Enum\n```c#\n[SourceReflection]\npublic enum TestEnum { A, B }\n```\n\n```c#\nvar type = SourceReflector.GetType(typeof(TestEnum));\nAssert.IsTrue(type.IsEnum);\nAssert.AreEqual(\"A\", type.DeclaredFields[0].Name);\nAssert.AreEqual(\"B\", type.DeclaredFields[1].Name);\nAssert.AreEqual(0, type.DeclaredFields[0].GetValue(null));\nAssert.AreEqual(1, type.DeclaredFields[1].GetValue(null));\n```\n\n## Array\n\nThe usage of `MakeArrayType` is similar to that of Runtime reflection.\n\n```c#\n[assembly: SourceReflectionType(typeof(int))]\n\nSourceTypeInfo type = SourceReflector.GetType\u003cint\u003e();\nSourceTypeInfo arrayType = type.MakeArrayType();\n\nint[] array = [1, 2];\nAssert.AreEqual(2, arrayType.GetRequriedProperty(\"Length\").GetValue(array));\n\narrayType.GetMethod(\"Set\")!.Invoke(array, [0, 2]);\nAssert.AreEqual(2, arrayType.GetMethod(\"Get\")!.Invoke(array, [0]));\n```\n\nOr adding `SourceReflectionTypeAttribute`\n\n```c#\n[assembly: SourceReflectionType(typeof(int[]))]\n```\n\n\n## Nullable Annotation\n\nSourceReflection supports `nullable` annotations,\n`nullable` annotations can be obtained for fields, properties, method return values, and parameters. It includes:\n- SourceNullableAnnotation.Annotated\n- SourceNullableAnnotation.NotAnnotated\n- SourceNullableAnnotation.None, Indicates that nullable is disabled in the current context.\n\n```c#\n[SourceReflection]\npublic class NullableAnnotationTestObject\n{\n    public string? Nullable { get; set; }\n    public string NotNullable { get; set; } = null!;\n\n    #nullable disable\n\n    public string DisableNullable { get; set; }\n}\n```\n\n```c#\nvar type = SourceReflector.GetType\u003cNullableAnnotationTestObject\u003e()!;\n\nAssert.AreEqual(SourceNullableAnnotation.Annotated, type.GetProperty(\"Nullable\").NullableAnnotation);\nAssert.AreEqual(SourceNullableAnnotation.NotAnnotated, type.GetProperty(\"NotNullable\").NullableAnnotation);\nAssert.AreEqual(SourceNullableAnnotation.None, type.GetProperty(\"DisableNullable\").NullableAnnotation);\n```\n\n## Required Member\n\n```c#\n[SourceReflection]\npublic class RequiredMemberTestObject\n{\n    public required int Property { get; set; } = 1;\n    public required int Field = 1;\n}\n```\n```c#\nvar type = SourceReflector.GetType(typeof(RequiredMemberTestObject));\nAssert.IsTrue(type.GetProperty(\"Property\").IsRequired);\nAssert.IsTrue(type.GetProperty(\"Field\").IsRequired);\n```\n\n## InitOnly Property\n```c#\n[SourceReflection]\npublic class InitOnlyPropertyTestObject\n{\n    public int Property { get; init; }\n}\n```\n```c#\nvar type = SourceReflector.GetType(typeof(InitOnlyPropertyTestObject));\nAssert.IsTrue(type.GetProperty(\"Property\").IsInitOnly);\n```\n\n## GenericEnumerableType Property\n```c#\n[SourceReflection]\npublic class EnumerablePropertyTestObject\n{\n    public IEnumerable\u003cstring\u003e Enumerable { get; init; }\n    public List\u003cstring\u003e List { get; init; }\n    public CustomList CustomList { get; init; }\n}\n\npublic class CustomList : IList\u003cint\u003e { }\n```\n```c#\nvar type = SourceReflector.GetType(typeof(EnumerablePropertyTestObject));\nAssert.IsTrue(type.GetProperty(\"Enumerable\").IsGenericEnumerableType);\nAssert.IsTrue(type.GetProperty(\"List\").IsGenericEnumerableType);\nAssert.IsTrue(type.GetProperty(\"CustomList\").IsGenericEnumerableType);\n```\n\n## GenericDictionaryType Property\n```c#\n[SourceReflection]\npublic class DictionaryPropertyTestObject\n{\n    public IDictionary\u003cstring,string\u003e Dictionary { get; init; }\n    public SortedDictionary\u003cstring,string\u003e SortedDictionary { get; init; }\n    public CustomDictionary CustomDictionary { get; init; }\n}\n\npublic class CustomDictionary : IDictionary\u003cint,object\u003e { }\n```\n```c#\nvar type = SourceReflector.GetType(typeof(DictionaryPropertyTestObject));\nAssert.IsTrue(type.GetProperty(\"Dictionary\").IsGenericDictionaryType);\nAssert.IsTrue(type.GetProperty(\"SortedDictionary\").IsGenericDictionaryType);\nAssert.IsTrue(type.GetProperty(\"CustomDictionary\").IsGenericDictionaryType);\n```\n\n## Create Instance\n\nThe `SourceReflector.CreateInstance` method has almost the same functionality and features as the `System.Activator.CreateInstance` method.\nIt supports parameter matching and parameter default values.\n\n```c#\n[SourceReflection]\npublic class CreateInstanceTestObject\n{\n    public CreateInstanceTestObject() { }\n    public CreateInstanceTestObject(byte a, string? c = \"abc\") { }\n    internal CreateInstanceTestObject(int a, int b) { }\n    protected CreateInstanceTestObject(long a, int c = 1, string? c = \"abc\") { }\n}\n```\n\n```c#\nvar o1 = SourceReflector.CreateInstance\u003cCreateInstanceTestObject\u003e(); // Call the first constructor.\nvar o2 = SourceReflector.CreateInstance\u003cCreateInstanceTestObject\u003e((byte)1); // Call the second constructor.\nvar o3 = SourceReflector.CreateInstance\u003cCreateInstanceTestObject\u003e(1, 2); // Call the third constructor.\nvar o4 = SourceReflector.CreateInstance\u003cCreateInstanceTestObject\u003e(1L); // Call the fourth constructor.\n//or use non-generic method\nvar o5 = SourceReflector.CreateInstance(typeof(CreateInstanceTestObject), 1); // Call the fourth constructor.\nvar o6 = SourceReflector.CreateInstance(typeof(CreateInstanceTestObject), 1, 2, \"abc\"); // Call the fourth constructor.\n\n```\n\n\n## Generic Definition\n\nCurrently, support for generic type definition is not yet available. The main issue lies in the handling of `MakeGenericType`. I am currently experimenting with more effective approaches, but there is no specific plan at the moment.\n\n```c#\n[assembly: SourceReflectionType(typeof(List\u003c\u003e))]\n\n[SourceReflection]\npublic class GenericTypeDefinitionTestObject\u003cT\u003e { }\n```\n\n```c#\n//Can not generate generic type definition info\nAssert.IsNull(SourceReflector.GetType\u003cList\u003c\u003e\u003e());\nAssert.IsNull(SourceReflector.GetType\u003cGenericTypeDefinitionTestObject\u003c\u003e\u003e());\n```\n\n## Generic Type\n\nCurrently, support for generic type definition is not yet available. The main issue lies in the handling of `MakeGenericType`. The source generation can handle handle known types.\n\n```c#\n[assembly: SourceReflectionType(typeof(List\u003cstring\u003e))]\n\nvar type = SourceReflector.GetRequiredType\u003cList\u003cstring\u003e\u003e();\nList\u003cstring\u003e list = [\"a\", \"b\"];\ntype.GetMethod(\"Add\")!.Invoke(list, [\"c\"]);\nAssert.AreEqual(3, type.GetProperty(\"Count\")!.GetValue(list));\n```\n\n## Generic Method\n\nIf the parameter type can be inferred and cast to the constraint type, they can be called using source generation\n\n```c#\n[SourceReflection]\npublic class GenericMethodTestObject\n{\n    //can't work (can not infer type parameter)\n    public T Invoke0\u003cT\u003e() =\u003e default!;\n\n    public T Invoke1\u003cT\u003e(T t) =\u003e t;\n    public T Invoke2\u003cT\u003e(T t) where T : ICloneable =\u003e t;\n    public T Invoke3\u003cT\u003e(T t) where T : notnull =\u003e t;\n    public T Invoke4\u003cT\u003e(T t) where T : Enum =\u003e t;\n\n    //can't work (Unable to infer type. unable to cast object to unmanaged object)\n    public T Invoke5\u003cT\u003e(T t) where T : unmanaged =\u003e t;\n\n    //can't work (Unable to infer type. unable to cast object to struct)\n    public T Invoke6\u003cT\u003e(T t) where T : struct =\u003e t;\n\n    public T Invoke7\u003cT\u003e(T t) where T : ICloneable, IComparable =\u003e t;\n    public T Invoke8\u003cT, K\u003e(T t, K k) where T : ICloneable where K : IComparable =\u003e t;\n\n    //can't work (Unable to infer type. unable to cast object to T[])\n    public T[] InvokeArray1\u003cT\u003e(T[] t) =\u003e t;\n}\n```\n\n```c#\nSourceTypeInfo type = SourceReflector.GetRequiredType\u003cGenericMethodTestObject\u003e();\nGenericMethodTestObject instance = new();\n\n// Success\ntype.GetMethod(\"Invoke1\").Invoke(instance, [1]);\ntype.GetMethod(\"Invoke2\").Invoke(instance, [1]);\ntype.GetMethod(\"Invoke3\").Invoke(instance, [1]);\ntype.GetMethod(\"Invoke4\").Invoke(instance, [Gender.Male]);\ntype.GetMethod(\"Invoke7\").Invoke(instance, [1]);\ntype.GetMethod(\"Invoke8\").Invoke(instance, [1, 2]);\n\n//Error (can not infer type parameter)\ntype.GetMethod(\"Invoke0\").Invoke(instance, []);\ntype.GetMethod(\"Invoke5\").Invoke(instance, [1]);\ntype.GetMethod(\"Invoke6\").Invoke(instance, [1]);\ntype.GetMethod(\"InvokeArray1\").Invoke(instance, [new int[] { 1 }]);\n```\n\nWhen the parameter type can be inferred and cast to the constraint type, the only option is to use runtime reflection through `MakeGenericMethod`, which will not be supported in AOT compilation.\n\n```c#\ntype.GetMethod(\"Invoke0\").MethodInfo.MakeGenericMethod([]).Invoke(instance);\n```\n\nEven if the type can be inferred and can be explicitly cast to a constrained type, this approach has its drawbacks. If the internal implementation of the method has type checks on the generic parameters, the result may not meet expectations.\n\n```c#\npublic class GenericMethodTestObject\n{\n    public string Invoke1\u003cT\u003e(T value) =\u003e typeof(T).Name;\n    public string Invoke2\u003cT\u003e(T value) where T : ICloneable =\u003e typeof(T).Name;\n}\n```\n\n```c#\nvar type = SourceReflector.GetRequiredType\u003cGenericMethodTestInferObject\u003e();\nGenericMethodTestInferObject instance = new();\nAssert.AreEqual(\"Object\", type.GetMethod(\"Invoke1\")!.Invoke(instance, [1]));\nAssert.AreEqual(\"ICloneable\", type.GetMethod(\"Invoke2\")!.Invoke(instance, [\"a\"]));\n```\n\n\n## Without `SourceReflectionAttribute`\n\nYou can also without using `SourceReflectionAttribute` for reflection\n\n```c#\npublic class Goods\n{\n    private int Id { get; set; }\n    public string Name { get; private set; }\n    public double Price { get; set; }\n\n    internal void Discount(double discount)\n    {\n        Price = Price * discount;\n    }\n}\n```\n\n```c#\n// Get TypeInfo and allow Runtime Reflection\nvar type = SourceReflector.GetType(typeof(Goods), true);\n\nvar goods = (Goods)type.GetConstructor([]).Invoke([]);\ntype.GetProperty(\"Id\").SetValue(goods, 1); // private property\ntype.GetProperty(\"Name\").SetValue(goods, \"book\"); // private property setter\ntype.GetProperty(\"Price\").SetValue(goods, 3.14); // public property\ntype.GetMethod(\"Discount\").Invoke(goods, [0.5]);\n```\n\nIt can work properly after AOT compilation. `DynamicallyAccessedMembers` allows tools to understand which members are being accessed during the execution of a program. \n\n## Use other attribute to mark SourceReflection\n\nYou can create a custom attribute to indicate to the source generator which types need to be reflected. \n\nEdit your project `.csproj`\n```xml\n\u003c!-- define your Attribute --\u003e\n\u003cPropertyGroup\u003e\n  \u003cDisplaySourceReflectionAttribute\u003eSystem.ComponentModel.DataAnnotations.DisplayAttribute\u003c/DisplaySourceReflectionAttribute\u003e\n\u003c/PropertyGroup\u003e\n\n\u003c!-- set property visible  --\u003e\n\u003c!-- property name must be endswith 'SourceReflectionAttribute'  --\u003e\n\u003cItemGroup\u003e\n  \u003cCompilerVisibleProperty Include=\"DisplaySourceReflectionAttribute\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\nNow you can use the `DisplayAttribute` to inform the source generator that you need to reflect it.\n\n```c#\n[System.ComponentModel.DataAnnotations.DisplayAttribute]\npublic class Goods\n{\n    private int Id { get; set; }\n    public string Name { get; private set; }\n    public double Price { get; set; }\n}\n```\n\n## System.Text.Json Adapter\n\nSupports `AOT` without JsonSerializerContext,\n`System.Text.Json` already provides a complete solution for AOT compilation, but in most cases, besides JSON serialization, there there are still many places where reflection is needed. Although different solutions can be selected for different scenarios, it may also result of more models or the marking of more attributes. SourceReflection can simplify this for JSON serialization.\n\n```powershell\nInstall-Package SourceGeneration.Reflection.SystemTextJson -Version 1.0.0-beta2.240523.1\n```\n```powershell\ndotnet add package SourceGeneration.Reflection.SystemTextJson --version 1.0.0-beta2.240523.1\n```\n\n```c#\nvar options = new JsonSerializerOptions\n{\n    TypeInfoResolver = new DefaultJsonTypeInfoResolver().WithSourceReflection(),\n};\n\nvar json = JsonSerializer.Serialize(new Goods(), options);\nvar goods = JsonSerializer.Deserialize\u003cModel\u003e(json, options);\n\n[SourceReflection]\npublic class Goods\n{\n    private int Id { get; set; }\n    public string Name { get; private set; }\n    public double Price { get; set; }\n}\n```\n\n## Performance \u0026 Optimization\n\n`SourceReflection` generates alternative reflection-based method invocations through source generator, for example:\n\n```c#\n// code generated\nnew SourcePropertyInfo()\n{\n    Name = \"Name\",\n    FieldType = typeof(string),\n    GetValue = instance =\u003e ((Goods)instance).Name,\n    SetValue = (instance, value) =\u003e ((Goods)instance).Name = (string)value,\n    //Other properties init\n}\n```\n\nFor public and internal members, this approach is used,\nwhile for protected and private members, runtime reflection is still used to accomplish it.\n\nYou can use `SourceReflection` get the runtime `MemberInfo`, the code like this:\n\n```c#\npublic class SourcePropertyInfo\n{\n    private PropertyInfo? _propertyInfo;\n    private Func\u003cobject, object?\u003e? _getMethod;\n\n    public PropertyInfo PropertyInfo\n    {\n        get =\u003e _propertyInfo ??= typeof(Goods).GetProperty(BindingFlags.NonPublic | BindingFlags.Instance, \"Id\");\n    }\n\n    public Func\u003cobject, object?\u003e GetValue\n    {\n        get =\u003e _getMethod ??= PropertyInfo.GetValue;\n        init =\u003e _getMethod = value;\n    }\n}\n```\n`SourceReflection` uses lazy evaluation, which means that reflection is only performed and the result is cached when you first retrieve it.\nYou don't need to worry about whether the user has marked an object with the `SourceReflectionAttribute`. You can use the `SourceReflection` to retrieve metadata or invoke methods in a generic way regardless of whether the attribute is used.\n`SourceReflection` globally caching all objects (Type, FieldInfo, PropertyInfo, MethodInfo, ConstructorInfo) in a static cache.\n\n## Samples\n\n- [Basic](https://github.com/SourceGeneration/Reflection/tree/main/samples/Basic) example demonstrates some basic uses of SourceReflection.\n\n- [Sytem.Text.Json Adapter](https://github.com/SourceGeneration/Reflection/tree/main/samples/SystemTextJson) example demonstrates how to uses `SourceReflection` for JsonSerializer .\n\n- [CsvExporter](https://github.com/SourceGeneration/Reflection/tree/main/samples/CsvWriter) is a csv file export sample.\n\n- [AutoMapper](https://github.com/SourceGeneration/Reflection/tree/main/samples/AutoMapper) is a object-object mapper sample.\n\n- [CustomLibrary](https://github.com/SourceGeneration/Reflection/tree/main/samples/CustomLibrary) example demonstrates how to use SourceReflection to publish your NuGet package and propagate your attributes.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsourcegeneration%2Freflection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsourcegeneration%2Freflection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsourcegeneration%2Freflection/lists"}