C# 我可以创建一个函数字典,它的第一个参数不同吗?
我有一堆静态方法,它们都有相同的签名,除了第一个参数的类型,例如:C# 我可以创建一个函数字典,它的第一个参数不同吗?,c#,.net,C#,.net,我有一堆静态方法,它们都有相同的签名,除了第一个参数的类型,例如: public static class ElementCreators { public static XmlElement CreateElementForString (string s, string name, string namespaceURI) { [...] } public static XmlElement CreateElem
public static class ElementCreators
{
public static XmlElement CreateElementForString
(string s, string name, string namespaceURI)
{
[...]
}
public static XmlElement CreateElementForDecimal
(Decimal d, string name, string namespaceURI)
{
[...]
}
}
我想编一本字典(或者某种可以在运行时修改的查找-人们应该能够添加自己的func,尽管一旦添加func,就不需要修改/删除它,并且给定类型永远不会有两个+func。基类型和派生类型可能有func,但在这种情况下,由用户注册它们,例如,在r中)(订单)根据类型发送,例如:
var funcs = new Dictionary<Type, Func<object, string, string, XmlElement>>();
funcs[typeof(string)] = ElementCreators.CreateElementForString;
funcs[typeof(Decimal)] = ElementCreators.CreateElementForDecimal;
这是可行的,但是a)丑陋,b)引入了一堆不必要的代理
泛型似乎不是一个选项,因为Func不能是开放类型t。类似地,dynamic不起作用,但我无论如何都不想使用它们(运行时成本)
我可以为每个方法引入一个级别的间接寻址,这样可以避免委托,但它并没有那么难看:
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都可用于此功能。如评论所述,并非如此。但我认为,您可以构建一个实现您想要的(相对类型安全、易于使用)的类
我认为除了调用时强制转换之外,您不会支付任何费用,但尚未对其进行分析以确认。如果您可以是完整的程序
静态的
/global,那么您可以使用非常快速的编译时字典(无查找)和泛型(无装箱):
类程序
{
void Main()
{
//设置默认值;lambda而不是方法组(allocs)
ElementCreators.CreateElement=(s,name,namespaceURI)
=>ElementCreators.CreateElement(s、名称、命名空间URI);
ElementCreators.CreateElement=(d,名称,命名空间URI)
=>ElementCreators.CreateElement(d,name,namespaceURI);
//召唤
xmlementxml=ElementCreators.CreateElement(“hello”、“name”、“ns”);
}
}
公共静态类ElementCreators
{
//如果为null,则可以更改属性get以抛出KeyNotFound
公共静态Func CreateElement{get;set;}
}
公共静态类ElementCreators
{
公共静态XmlElement CreateElement(字符串s、字符串名称、字符串名称空间URI)
{
返回null;
}
公共静态XmlElement CreateElement(十进制d、字符串名称、字符串名称空间URI)
{
返回null;
}
}
以另一种方式推理;假设您确实成功地将词典从类型
转换为某物
。您将如何调用某物
?如果要通过反射(或动态)调用它,那么字典的值类型是什么又有什么关系呢?它可以是对象
,或动态
或系统。委托
或其他;在call站点上没有编译时安全性,那么为什么要在字典中担心它呢?@EricLippert当你这样说的时候,它是有道理的。事实上,当用户说传入的对象可以转换为参数类型时,这将挫败编译器检查并依赖于用户没有撒谎,但语言通过显式转换而不是任何隐式转换来支持这一点。我会想出一个更好的方法,至少可以强制执行编译器检查。是的,处理T4模板/构建任务/自定义工具来实现这种额外的类型安全性实在是太多了。只需创建一个助手方法,比如private void SetDelegate(Func f)=>funcs[typeof(T)]=(o,s1,s2)=>f((T)o,s1,s2)代码>,编写一些好的单元测试,并完成它。委托类型可以有逆变,例如Func
在T1
中是逆变的()。另外,从方法到委托(可能是非泛型委托)的转换可以是反向的,但我同样认为这不会有帮助。在这两种情况下,object
到decimal
之间的差异都不会有帮助,因为后者是一种值类型(在C#/.NET中不受任何差异的支持)。我这样做非常有效,除了FxCop/编译器抱怨类型定义上的泛型参数外,它工作得非常好。这很有趣,谢谢!是的,字典是静态的,尽管我需要能够在运行时添加到字典中。也就是说,如果它是在编译时设置的(其思想是用户可以“重写”funcs),那么这实际上可以解决常见情况。明天就试试看!这个问题是一个静态类,所以如果不创建一个新的静态类,就不能有多个;因此,这是一个非常全局类型的字典(永远不会是GC的),我同意Kevin的建议,因为它更适合我的用例,但这里的工作也非常好!是的,它不太灵活,因为它只适用于非常狭窄的用例;但是如果你有那个用例,它会非常快。但是,对于库来说,这是一种糟糕的方法(由于单个实例)
public static XmlElement CreateElementForString(object s, string name, string namespaceURI)
=> CreateElementForString((string)s, name, namespaceURI);
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);
}
}
// 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");
}
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;
}
}