是否可以创建第一个参数有所不同的Funcs字典? [英] Can I create a Dictionary of Funcs that vary in their first argument?

查看:34
本文介绍了是否可以创建第一个参数有所不同的Funcs字典?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一堆静态方法,除了第一个参数的类型外,它们都具有相同的签名,例如:

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)
    {
        [...]
    }
}

我想创建一个Dictionary(或某种可以在运行时修改的查找-人们应该能够添加自己的func,尽管一旦添加func,就无需修改/删除后,给定类型的功能就永远不会有两个以上的功能.基本类型和派生类型可能都没有功能,但是在这种情况下,用户可以根据类型进行注册(例如,以正确的顺序),然​​后根据类型进行分配,例如:

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;

现在,这行不通,因为委托之间没有矛盾,因此编译器抱怨 CS0123'CreateElementForString'的重载不匹配委托'Func< object,string,string,XmlElement>'.

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

可以,但是a)丑陋,b)引入了一堆不必要的委托.

That works, but is a) ugly and b) introduces a bunch of unnecessary delegates.

泛型似乎不是一个选择,因为Func不能是开放类型T.类似地,动态也不起作用,但我还是不想使用它们(运行时成本).

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

当然,我可以尝试自动化类似的操作(T4模板,预构建任务,自定义构建操作等)

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和C#7.2均可用于此目的.

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.

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(...)(大概调用多于注册).

You do have to type the Add(...) call, it can't be inferred), but the Invoke(...) can be inferred (and presumably there are more invocations than registrations).

// 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");
}

除了调用时,我认为您不需要支付任何费用,但没有进行概要确认.

I don't think you'll pay for anything besides the cast on invoke, but haven't profiled to confirm.

这篇关于是否可以创建第一个参数有所不同的Funcs字典?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆