我有一堆静态方法,除了第一个参数的类型外,都有相同的签名,例如:
public static class ElementCreators
{
public static XmlElement CreateElementForString
(string s, string name, string namespaceURI)
{
[...]
}
public static XmlElement CreateElementForDecimal
(Decimal d, string name, string namespaceURI)
{
[...]
}
}
我想创建一个Dictionary(或某种可以在运行时修改的查找 - 人们应该能够添加自己的func,但是一旦添加了func,它就不需要修改/删除,并且给定类型永远不会有两个+ func。可能有基类和派生类型的函数,但在这种情况下,由用户注册它们,例如,按照正确的顺序)根据类型进行调度,例如:
var funcs = new Dictionary<Type, Func<object, string, string, XmlElement>>();
funcs[typeof(string)] = ElementCreators.CreateElementForString;
funcs[typeof(Decimal)] = ElementCreators.CreateElementForDecimal;
现在,这不起作用,因为委托之间没有矛盾,所以编译器抱怨CS0123 No overload for 'CreateElementForString' matches delegate 'Func<object, string, string, XmlElement>'
。
一种选择是创建另一个委托作为中间人:
funcs[typeof(string)] =
(o,s1,s2) => ElementCreators.CreateElementForString((string)o, s1, s2);
这有效,但是a)丑陋,b)介绍了一堆不必要的代表。
泛型似乎不是一个选项,因为Func不能是开放类型T.同样,动态不起作用,但我不想使用这些(运行时成本)。
我可以为每个方法引入一个间接级别,这可以避免委托,但它不会那么难看:
public static XmlElement CreateElementForString(object s, string name, string namespaceURI)
=> CreateElementForString((string)s, name, namespaceURI);
当然,我可以尝试自动化类似的东西(T4模板,预构建任务,自定义构建操作等)。
但在我这样做之前,我想知道是否有更好的方式让我忽略了?
Visual Studio 2017,.NET 4.7.1和C#7.2都可用于此。
答案 0 :(得分:5)
正如评论所涵盖的那样,并非如此。但是我认为你可以建立一个完成你想要的课程(相对类型安全,使用愉快)。
public class DelegateDictionary
{
Dictionary<Type, Delegate> Lookup;
public DelegateDictionary()
{
Lookup = new Dictionary<System.Type, Delegate>();
}
public void Add<T>(Func<T, string, string, XmlElement> mtd)
{
Lookup.Add(typeof(T), mtd);
}
public XmlElement Invoke<T>(T value, string name, string namespaceURI)
{
if (!Lookup.TryGetValue(typeof(T), out var del)) throw new InvalidOperationException($"No delegate registered for {typeof(T).Name}");
var typedDel = (Func<T, string, string, XmlElement>)del;
return typedDel(value, name, namespaceURI);
}
}
您必须键入Add(...)
调用,无法推断,但可以推断Invoke(...)
(并且可能会有更多的调用而不是注册)。
// example usage
{
var dict = new DelegateDictionary();
dict.Add<string>(ElementCreators.CreateElementForString);
dict.Add<Decimal>(ElementCreators.CreateElementForDecimal);
dict.Invoke("stringValue", "myName", "what-even-is-a-namespace");
dict.Invoke(1.0m, "myName", "what-even-is-a-namespace");
}
我不认为你会在调用时除了演员之外支付任何费用,但还没有说明确认。
答案 1 :(得分:4)
如果您可以使用整个程序static
/ global,那么您可以使用非常快速的编译时字典(无查找)和泛型(无拳击):
class Program
{
void Main()
{
// Set up defaults; lambda rather than Method group (allocs)
ElementCreators<string>.CreateElement = (s, name, namespaceURI)
=> ElementCreators.CreateElement(s, name, namespaceURI);
ElementCreators<Decimal>.CreateElement = (d, name, namespaceURI)
=> ElementCreators.CreateElement(d, name, namespaceURI);
// Call
XmlElement xml = ElementCreators<string>.CreateElement("hello", "name", "ns");
}
}
public static class ElementCreators<T>
{
// Can change property get to throw KeyNotFound if null
public static Func<T, string, string, XmlElement> CreateElement { get; set; }
}
public static class ElementCreators
{
public static XmlElement CreateElement(string s, string name, string namespaceURI)
{
return null;
}
public static XmlElement CreateElement(Decimal d, string name, string namespaceURI)
{
return null;
}
}