{"id":30203100,"url":"https://github.com/ent3m/multicastfunc","last_synced_at":"2025-10-04T23:51:45.209Z","repository":{"id":309539308,"uuid":"944126455","full_name":"ent3m/MulticastFunc","owner":"ent3m","description":"MulticastFunc is an alternative to Func delegates that makes retrieving the results of all invocations simple and efficient.","archived":false,"fork":false,"pushed_at":"2025-08-22T04:45:11.000Z","size":79,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-22T06:27:21.105Z","etag":null,"topics":["csharp","csharp-library","delegate","dotnet","utility","utility-library"],"latest_commit_sha":null,"homepage":"https://ent3m.github.io/MulticastFunc/","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/ent3m.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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,"zenodo":null}},"created_at":"2025-03-06T20:38:23.000Z","updated_at":"2025-08-22T04:45:15.000Z","dependencies_parsed_at":"2025-08-12T13:52:05.373Z","dependency_job_id":"dc8be69b-5231-4e43-9db8-521c5881b567","html_url":"https://github.com/ent3m/MulticastFunc","commit_stats":null,"previous_names":["ent3m/multicastfunc"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ent3m/MulticastFunc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ent3m%2FMulticastFunc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ent3m%2FMulticastFunc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ent3m%2FMulticastFunc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ent3m%2FMulticastFunc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ent3m","download_url":"https://codeload.github.com/ent3m/MulticastFunc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ent3m%2FMulticastFunc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278391190,"owners_count":25978944,"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-10-04T02:00:05.491Z","response_time":63,"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-library","delegate","dotnet","utility","utility-library"],"created_at":"2025-08-13T11:16:02.153Z","updated_at":"2025-10-04T23:51:45.204Z","avatar_url":"https://github.com/ent3m.png","language":"C#","readme":"# Description\nMulticastFunc is designed to be an alternative to Func. It provides a simple and efficient way to to retrieve the return values of all invocations instead of only the final invocation.\n\n# The Problem with Func\nTo retrieve the results of all invocations in a MulticastDelegate, one must get a list of invocations, cast and invoke each delegate individually, and store the results in an array.\nThe code looks like this with LINQ:\n```csharp\nT[]? results = myDelegate?.GetInvocationList().Cast\u003cFunc\u003cT\u003e\u003e().Select(f =\u003e f.Invoke()).ToArray();\n```\nYou can create an extension method to avoid typing that everytime. However, the allocation cost of `GetInvocationList` *cannot* be avoided. Invoking a `Func\u003cT\u003e` or other MulticastDelegates this way is slow and generates a lot of garbage over time.\u003c/br\u003e\u003c/br\u003e\n\n`MulticastFunc` solves this problem by having `Invoke` return an array of results without compromising on [performance](#Benchmarks). In addition, it behaves similarly to a delegate with its immutability and add/remove syntax, making it a suitable replacement for any `Func` delegate.\n\n# Usage\nAdding and removing:\n```csharp\nmulticastFunc += MyMethod;\nmulticastFunc += () =\u003e \"Hello World\";\nmulticastFunc -= MyMethod;\nmulticastFunc -= MyFunc;\n```\n\nInvoking:\n```csharp\nT[]? results = multicastFunc?.Invoke();\n```\n\nUse as a backing field for events:\n```csharp\npublic event Func\u003cstring\u003e EventHappened\n{\n  add =\u003e multicastFunc += value;\n  remove =\u003e multicastFunc -= value;\n}\nprivate MulticastFunc\u003cstring\u003e? multicastFunc = default;\n```\n\nGet delegate count:\n```csharp\nint count = multicastFunc.Count;\n```\n\nStore the results in a buffer:\n```csharp\nTask[] buffer = ArrayPool\u003cTask\u003e.Shared.Rent(multicastFunc.Count);\n\nint written = multicastFunc.Invoke(buffer);\n// or\nReadOnlySpan\u003cTask\u003e tasks = multicastFunc.Invoke(buffer.AsSpan());\n```\n\n# Installation\nInstall with [nuget](https://www.nuget.org/packages/MulticastFunc/) or download and copy the project into your solution.\n\n# Technical Details\nMulticastFunc maintains a `Delegate[]` that is updated whenever a function is added or removed. Invoking consists of iterating over the delegate, invoking each function, and storing the results, bypassing `GetInvocationList`. This makes the invocation process fast and garbage-free, especially if the overload that accepts a buffer is used.\u003c/br\u003e\u003c/br\u003e\nIt overloads the `+` and `-` operators to imitate the add/remove behavior of a delegate. Similar to a delegate, it is immutable, meaning invoking is thread-safe. The trade off is that adding and removing is slower with a MulticastFunc than with a delegate.\u003c/br\u003e\u003c/br\u003e\nAnother limitation is that, because the compiler does not recognize MulticastFunc as a delegate type, you cannot assign a method group to it directly. This means that `multicastFunc = MyMethod` is not allowed, but `multicastFunc += MyMethod` is allowed.\n\n# Benchmarks\nThe performance of `MulticastFunc.Invoke()` is roughly equals to that of `Func.Invoke()` and is *~6 times* faster than invoking with LINQ.\n| Method                          | DelegateCount | Mean         | Ratio | Allocated | Alloc Ratio |\n|-------------------------------- |-------------- |-------------:|------:|----------:|------------:|\n| Invoke_Func_Linq                | 25            |   332.711 ns |  6.17 |     792 B |        6.19 |\n| Invoke_Func                     | 25            |    55.636 ns |  1.03 |         - |        0.00 |\n| Invoke_MulticastFunc            | 25            |    53.958 ns |  1.00 |     128 B |        1.00 |\n| Invoke_MulticastFunc_SpanBuffer | 25            |    39.281 ns |  0.73 |         - |        0.00 |\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fent3m%2Fmulticastfunc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fent3m%2Fmulticastfunc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fent3m%2Fmulticastfunc/lists"}