this doesn't really address OP's point, where in JS you can do:
const foo = () => doSomething;
foo.help = "this is a description of the function";
const commands = [foo];
// print help
commands.forEach(c => console.log(c.name, c.help || "No help is available for this function");
Presumably this isn't possible in C# because it's statically typed, so the object returned by "() => doSomething" can't be converted into one that supports adding more properties?
.NET/C# has a `dynamic` type (aka `ExpandoObject`). That would be one way to do it (but would require casting to invoke). It's not exactly the same since you'd assign the `Func`/`Action` to a property of the `dynamic`. `dynamic` is generally avoided due to how easy it is to get into a pickle with it and also performance issues.
An alternate in this case is probably to return a tuple which I think is just as good/better.
Example:
var log = (object message) => Console.WriteLine(message);
var foo = () => log("Hello, World");
var fn1 = (foo, "This is the help text");
var commands = new[] { fn1 };
commands.ToList().ForEach(c => {
var (fn, help) = c;
log(fn.Method.Name);
log(help ?? "No help is available for this function");
});
The tuple can also take named properties like this:
var log = (object message) => Console.WriteLine(message);
var foo = () => log("Hello, World");
var commands = new (Action fn, string? help)[] {
(foo, "This is the help text"),
(foo, null)
};
commands.ToList().ForEach(c => {
log(c.fn.Method.Name);
log(c.help ?? "No help is available for this function");
});
var log = (object message) => Console.WriteLine(message);
var foo1 = () => log("Hello, World");
var foo2 = () => log("Hello, Neighbor");
var bar = new[] {
new {
doSomething = foo1,
help = "This is foo1's help text"
},
new {
doSomething = foo2,
help = "This is foo2's help text"
},
};
bar.ToList().ForEach(b => {
var (fn, help) = (b.doSomething, b.help);
fn();
log(help);
});
Very much looking forward to this since it gives you a lot of the same power of the JavaScript map/TS `Record`.
> ...because it's statically typed
While this is true, the `dynamic`/`ExpandoObject` is an oddity and lets you do weird things like multiple dispatch on .NET (https://charliedigital.com/2009/05/28/visitor-pattern-in-c-4...). But C# has a bunch of compiler tricks with regards to anonymous types that can mimic JS objects to some extent. The tuple type is probably a better choice in most cases, however.