{"id":13629400,"url":"https://github.com/krasin-ga/matryoshki","last_synced_at":"2026-01-14T05:04:19.656Z","repository":{"id":153255964,"uuid":"625459978","full_name":"krasin-ga/matryoshki","owner":"krasin-ga","description":"High-performance zero-reflection approach to C# metaprogramming","archived":false,"fork":false,"pushed_at":"2024-09-29T19:01:37.000Z","size":204,"stargazers_count":32,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-12-31T10:13:47.780Z","etag":null,"topics":["csharp","csharp-sourcegenerator","dotnet"],"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/krasin-ga.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}},"created_at":"2023-04-09T07:06:22.000Z","updated_at":"2025-12-22T20:22:42.000Z","dependencies_parsed_at":"2024-01-06T02:10:00.073Z","dependency_job_id":"9151fea3-9c37-4d18-80c3-536131cf31d1","html_url":"https://github.com/krasin-ga/matryoshki","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/krasin-ga/matryoshki","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasin-ga%2Fmatryoshki","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasin-ga%2Fmatryoshki/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasin-ga%2Fmatryoshki/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasin-ga%2Fmatryoshki/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/krasin-ga","download_url":"https://codeload.github.com/krasin-ga/matryoshki/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasin-ga%2Fmatryoshki/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28410144,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"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":["csharp","csharp-sourcegenerator","dotnet"],"created_at":"2024-08-01T22:01:09.462Z","updated_at":"2026-01-14T05:04:19.640Z","avatar_url":"https://github.com/krasin-ga.png","language":"C#","funding_links":[],"categories":["Content","Source Generators"],"sub_categories":["33. [Matryoshki](https://ignatandrei.github.io/RSCG_Examples/v2/docs/Matryoshki) , in the [Interface](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#interface) category","Metaprogramming"],"readme":"﻿# Matryoshki \n[![Matryoshki Nuget](https://img.shields.io/nuget/v/Matryoshki?color=1E9400\u0026label=Matryoshki\u0026style=flat-square)](https://www.nuget.org/packages/Matryoshki/) [![Matryoshki.Abstractions Nuget](https://img.shields.io/nuget/v/Matryoshki.Abstractions?color=1E9400\u0026label=Matryoshki.Abstractions\u0026style=flat-square)](https://www.nuget.org/packages/Matryoshki.Abstractions/) [![Matryoshki.Generators Nuget](https://img.shields.io/nuget/v/Matryoshki.Generators?color=1E9400\u0026label=Matryoshki.Generators\u0026style=flat-square)](https://www.nuget.org/packages/Matryoshki.Generators/) \n\n\n\u003cimg src=\"assets/matryoshki.svg\" align=\"right\" /\u003e \"Matryoshki\" (Матрёшки, Matryoshkas) is a metaprogramming framework based on C# source generators.\n\n\n#### Key Features\n* Define type-agnostic templates and create decorators based on them:\n `Decorate\u003cIFoo\u003e.With\u003cLoggingAdornment\u003e().Name\u003cFooWithLogging\u003e()`\n+ Extract interfaces and automatically generate adapters from classes: `From\u003cBar\u003e.ExtractInterface\u003cIBar\u003e()`.\n\n## Getting Started\n\n### Installation\n\nThe first step is to add package to the target project:\n\n``` bash\ndotnet add package Matryoshki\n```\n\nOnce the package is installed, you can proceed with creating adornments.\n\n\n### Adornments\n\n\u003cimg src=\"assets/flower.png\" width=\"48\" align=\"left\" /\u003e \n\nAdornments act as blueprints for creating type-agnostic decorators. They consist of a method template and can contain arbitrary members. Rather than being instantiated as objects, the code of adornment classes is directly injected into the decorator classes.\n\nTo create an adornment you need to create a class that implements `IAdornment`. As a simple example, you can create an adornment that outputs the name of the decorated member to the console:\n\n``` C#\npublic class HelloAdornment : IAdornment\n{\n    public TResult MethodTemplate\u003cTResult\u003e(Call\u003cTResult\u003e call)\n    {\n        Console.WriteLine($\"Hello, {call.MemberName}!\");\n        return call.Forward();\n    }\n}\n```\n\nWhen creating a decorated method, `call.Forward()` will be replaced with a call to the implementation. And `TResult` will have the type of the actual return value. For `void` methods, a special type `Nothing` will be used.\n\n\u003cdetails\u003e\n  \u003csummary\u003eA more complex example\u003c/summary\u003e\n\n\nAn adornment for logging can serve as a slightly closer example to real-world usage:\n``` C#\npublic class LoggingAdornment : IAdornment\n{\n    private readonly ILogger\u003cExceptionLoggingAdornment\u003e _logger;\n\n    public LoggingAdornment(ILogger\u003cExceptionLoggingAdornment\u003e logger)\n    {\n        _logger = logger;\n    }\n\n    public TResult MethodTemplate\u003cTResult\u003e(Call\u003cTResult\u003e call)\n    {\n        try\n        {\n            if(_logger.IsEnabled(LogLevel.Debug))\n                _logger.LogDebug(\"Executing {Type}.{Member}\", GetType().Name, call.MemberName);\n\n            var result = call.Forward();\n\n            if (_logger.IsEnabled(LogLevel.Debug))\n                _logger.LogDebug(\"Successfully executed {Type}.{Member}: {Result}\", GetType().Name, call.MemberName, result);\n\n            return result;\n        }\n        catch (Exception exception)\n        {\n            _logger.LogError(\n                exception,\n                \"Error executing {Type}.{Member}({Arguments})\",\n                GetType().Name,\n                call.MemberName,\n                string.Join(\",\", call.GetArgumentsOfType\u003cobject\u003e()));\n\n            throw;\n        }\n    }\n}\n```\n\n  \n\u003c/details\u003e\n\n#### Asynchronous method templates\n\nAsynchronous templates can be defined by implementing the `AsyncMethodTemplate` method, which will be used to decorate methods that return `Task` or `ValueTask`. \n\nNote that asynchronous templates are optional, and async methods will still be decorated because an `AsyncMethodTemplate` will be automatically created from the `MethodTemplate` by awaiting the `Forward*` method invocations.\n\nMore tips for writing adornments can be found here: [tips](Tips.md).\n\n\n### Decoration\n\nOnce we have an adornment, we can create our first matryoshkas.\n\n\u003cdetails\u003e\n\u003csummary\u003eSuppose we have two interfaces that we would like to apply our HelloAdornment to.\u003c/summary\u003e\n\n``` C#\ninterface IFoo\n{\n    object Foo(object foo) =\u003e foo;\n}\nrecord Foo : IFoo;\n\ninterface IBar\n{\n    Task BarAsync() =\u003e Task.Delay(0);\n}\nrecord Bar : IFoo;\n```\n\n\u003c/details\u003e\n\nTo create matryoshkas, you just need to write their specification in any appropriate location:\n\n``` C#\nMatryoshka\u003cIFoo\u003e\n    .With\u003cHelloAdornment\u003e()\n    .Name\u003cFooMatryoshka\u003e();\n\nDecorate\u003cIBar\u003e // you can use Decorate\u003c\u003e alias if you prefer\n    .With\u003cHelloAdornment\u003e()\n    .Name\u003cBarMatryoshka\u003e();\n```\n\nDone! Now we can test the generated classes:\n\n``` C#\nvar fooMatryoshka = new FooMatryoshka(new Foo());\nvar barMatryoshka = new BarMatryoshka(new Bar());\n\nfooMatryoshka.Foo(); // \"Hello, Foo!\" will be written to console\nbarMatryoshka.Bar(); // \"Hello, Bar!\" will be written to console\n```\n\nIn a production environment, you will likely prefer to use DI containers that support decoration (Grace, Autofac, etc.) or libraries like [Scrutor](https://github.com/khellang/Scrutor). Here's an example of using matryoshkas together with Scrutor:\n\n``` C#\nusing Scrutor;\nusing Matryoshki.Abstractions;\n\npublic static class MatryoshkaScrutorExtensions\n{\n    public static IServiceCollection DecorateWithMatryoshka(\n        this IServiceCollection services,\n        Expression\u003cFunc\u003cMatryoshkaType\u003e\u003e expression)\n    {\n        var matryoshkaType = expression.Compile()();\n\n        services.Decorate(matryoshkaType.Target, matryoshkaType.Type);\n\n        return services;\n    }\n\n    public static IServiceCollection DecorateWithNestedMatryoshkas(\n        this IServiceCollection services,\n        Expression\u003cFunc\u003cMatryoshkaTypes\u003e\u003e expression)\n    {\n        var matryoshkaTypes = expression.Compile()();\n\n        foreach (var type in matryoshkaTypes)\n            services.Decorate(matryoshkaTypes.Target, type);\n\n        return services;\n    }\n}\n\ninternal static class Example\n{\n    internal static IServiceCollection DecorateBar(\n        this IServiceCollection services)\n    {\n        return services.DecorateWithMatryoshka(\n            () =\u003e Matryoshka\u003cIBar\u003e.With\u003cHelloAdornment\u003e());\n    }\n}\n```\n\n### Chains of decorations with INesting\u003cT1, ..., TN\u003e\n\nReusable decoration chains can be described by creating a type that implements `INesting\u003cT1, ..., TN\u003e`:\n\n``` C#\npublic record ObservabilityNesting : INesting\u003cMetricsAdornment, LoggingAdornment, TracingAdornment\u003e;\n```\n\nYou can generate the classes using it as follows:\n\n``` C#\nstatic IServiceCollection DecorateFoo(IServiceCollection services)\n{\n    //assuming that you are using MatryoshkaScrutorExtensions\n    return services.DecorateWithNestedMatryoshkas(\n        () =\u003e Matryoshka\u003cIBar\u003e.WithNesting\u003cObservabilityNesting\u003e());\n}\n```\n\nIt is not possible to assign names to the classes when using `INesting`. The generated types will be located in the `MatryoshkiGenerated.{NestingName}` namespace and have names in the format **TargetTypeName***With***AdornmentName**.\n\n## Limitations\n\n* Do not use a variable named `value`, as this can conflict with a property setter.\n* The `call` parameter should not be passed to other methods.\n* `default` cannot be used without specifying a type argument.\n* To apply decorations, the members must be abstract or virtual. To surpass this limitation you can generate an interface with expression `From\u003cTClass\u003e.ExtractInterface\u003cTInterface\u003e()` and then decrorate `TInterface`.\n* The decoration expression must be computable at compile time and written with a single statement\n* Pattern matching will not always work\n\n## License\n\nThis project is licensed under the [MIT license](LICENSE).\n\n\n\n## Quick links\n\n\n* [Tips](Tips.md)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrasin-ga%2Fmatryoshki","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrasin-ga%2Fmatryoshki","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrasin-ga%2Fmatryoshki/lists"}