I have a bunch of static methods that all have the same signature, except for the type of their first argument, for example:
public static class ElementCreators
{
public static XmlElement CreateElementForString
(string s, string name, string namespaceURI)
{
[...]
}
public static XmlElement CreateElementForDecimal
(Decimal d, string name, string namespaceURI)
{
[...]
}
}
I want to create a Dictionary (or some sort of lookup that can be modified at runtime - people are supposed to be able to add their own funcs, though once a func is added, it doesn't need to be modified/removed, and there are never two+ funcs for a given type. There might be funcs for base and derived types, but in that case, it's up to the user to register them e.g., in the right order) to dispatch based on the type, e.g.:
var funcs = new Dictionary<Type, Func<object, string, string, XmlElement>>();
funcs[typeof(string)] = ElementCreators.CreateElementForString;
funcs[typeof(Decimal)] = ElementCreators.CreateElementForDecimal;
Now, this doesn't work, as there is no contravariance between delegates, so the compiler complains that CS0123 No overload for 'CreateElementForString' matches delegate 'Func<object, string, string, XmlElement>'.
One option is to create another delegate as the middle-man:
funcs[typeof(string)] =
(o,s1,s2) => ElementCreators.CreateElementForString((string)o, s1, s2);
That works, but is a) ugly and b) introduces a bunch of unnecessary delegates.
Generics don't seem to be an option, because the Func can not be of an open type T. Similarly, dynamic doesn't work, but I don't want to use these anyway (runtime cost).
I could introduce a level of indirection for each method, which avoids the delegate, but it isn't any less ugly:
public static XmlElement CreateElementForString(object s, string name, string namespaceURI)
=> CreateElementForString((string)s, name, namespaceURI);
And of course, I could try automating something like that (T4 templates, Pre-Build task, custom Build Action, etc.)
But before I do that, I wonder if there's a better way that I've overlooked?
Visual Studio 2017, .NET 4.7.1, and C# 7.2 are all available for this.
As the comments cover, not really. But you can build a class that accomplishes what you want (relatively type safe, pleasant to use) I think.
You do have to type the
Add(...)call, it can't be inferred), but theInvoke(...)can be inferred (and presumably there are more invocations than registrations).I don't think you'll pay for anything besides the cast on invoke, but haven't profiled to confirm.