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;
    }
}