Dictionary <t,func =“”>:如何使用T作为Func的泛型类型?

时间:2017-05-19 14:44:40

标签: c# dictionary generics func

我不知道如何清楚表达。

我有这个界面:

interface IConverter
{
    Dictionary<Type, Func<string, object>> ConversionMethods { get; }
}

基本上,它定义了一个契约,说实现它的类应该为它使用的所有自定义类型提供转换方法(无论是枚举还是其他任何东西)。

是否可以将object的泛型类型中的Func替换为相应的字典键的类型(因此不可能有两种不匹配的类型)?

我认为这是不可能的,但替代方案有点烦人(使用dynamicobject,创建专门的字典......)。

编辑1:虚构的使用例子

interface IConverter
{
    Dictionary<Type, Func<string, object>> GetConversionMethods();
}

enum A
{
    AA,AB,AC
}

enum B
{
    BA, BB, BC
}

class blah : IConverter
{
    public Dictionary<Type, Func<string, object>> GetConversionMethods()
    {
        var d = new Dictionary<Type, Func<string, object>>
        {
            {
                typeof(A),
                (s) =>
                    {
                        // here, I could return whatever I want because the 'Func' returns 'object'
                        return s == "AA" ? A.AA : s == "AB" ? A.AB : A.AC;
                    }
            },
            {
                typeof(B),
                (s) =>
                    {
                        // same
                        return s == "BA" ? B.BA : s == "BB" ? B.BB : B.BC;
                    }
            }
        };
        return d;
    }

    void blahah()
    {
        // and here, I also get an `object`, where I would like to have a A
        GetConversionMethods()[typeof(A)]("123");
    }
}

2 个答案:

答案 0 :(得分:2)

这取决于您可以在多大程度上更改签名,但至少接口可以使用泛型类型强制执行。如何强加类型地添加转换器是实施者的责任,而不是调用者的责任。

实现本身可以使用任何构造来提供转换器。事件if ... else,但是为了使用字典下的字典,可以使用类型Dictionary<Type, Delegate>的字典,其中可以添加强类型的转换器。下面的示例使用帮助器set<T>函数来确保以预期的方式设置字典。

interface IConverter
{
    Func<string,T> GetConverter<T>(); //the method returned is always strongly typed, so the caller is never responsible for type checking
}

enum A{AA,AB,AC}    
enum B{BA, BB, BC}

class blah : IConverter
{
    public Func<string,T> GetConverter<T>()
    {
        if(methods.TryGetValue(typeof(T), out var fn)) //side note, out var fn will not work in older visual studio versions. In that case declare fn before this line
            return (Func<string,T>)fn; //the set<T> method ensures that this conversion is safe
        throw new NotImplementedException(); 
    }

    public blah()
    {
        set<A>(s => s == "AA" ? A.AA : s == "AB" ? A.AB : A.AC); //copied from the example. Enum.Parse could perhaps be used instead
        set<B>(s => s == "BA" ? B.BA : s == "BB" ? B.BB : B.BC);
    }

    Dictionary<Type, Delegate> methods= new Dictionary<Type, Delegate>(); // Delegate can be used as a type to handle all lambda's. It's the implementers responsibility to handle with care. Something like the set<T> helper method is recommended

    void set<T>(Func<string,T> fn) //helper method to assign the strongly typed methods to the specific type
    {
        methods[typeof(T)] = fn;
    }
}

static void blahah()
{
    new blah().GetConverter<A>()("123");
}

答案 1 :(得分:1)

这有点复杂,但它确实有效。

首先,您需要在类中封装转换Func,以便您可以更轻松地处理它们,而不会暴露所有不同类型的参数。然后,您需要定义接口或基类以隐藏它们导致问题的各种通用参数,并允许您将不同的转换器放在同一个集合中。然后,您需要各种转换器的方法来指示它们使用的类型,而不直接使用这些类型参数。然后你只需要在一个类中将它全部包装在一个方法中,该方法可以根据需要找到合适的转换器。

我将引导您完成它。

首先,这个基类将是我们处理转换器的方式,而不用担心它的泛型类型参数,但仍然知道它使用的类型。

public abstract class OneWayTypeConverterBase : IConvertFromType, IConvertToType
{
    public abstract Type AcceptsType { get; }
    public abstract Type ReturnsType { get; }
}

现在我们从该基类继承。这是完成转换实际工作的类;你可以使用lambda来实例化它,它可以执行你需要的任何转换操作。请注意,它实现了我们在上面定义的属性。

public class OneWayTypeConverter<TSource, TTarget> : OneWayTypeConverterBase
{
    public OneWayTypeConverter(Func<TSource, TTarget> conversionMethod)
    {
        _conversionMethod = conversionMethod;
    }

    public override Type AcceptsType => typeof(TSource);
    public override Type ReturnsType => typeof(TTarget);

    private readonly Func<TSource, TTarget> _conversionMethod;

    public TTarget Convert(TSource sourceObject)
    {
        return _conversionMethod(sourceObject);
    }
}

现在我们需要一个地方来容纳所有这些,因此消费代码有一个入口点。为简单起见,我接受了一个平面转换器集合,然后将它们全部归档到嵌套字典中,以便以后可以进行查找而无需一直调用typeof

public class TypeConverter
{
    public TypeConverter(IEnumerable<OneWayTypeConverterBase> converters)
    {
        _converters = converters
            .GroupBy(x => x.AcceptsType)
            .ToDictionary(
                kSource => kSource.Key,
                vSource => vSource
                    .ToDictionary(kTarget => kTarget.ReturnsType, vTarget => vTarget));
    }

    private Dictionary<Type, Dictionary<Type, OneWayTypeConverterBase>> _converters;

    public TTarget ConvertType<TSource, TTarget>(TSource sourceObject)
    {
        Dictionary<Type, OneWayTypeConverterBase> baseConverters;

        if (_converters.TryGetValue(sourceObject.GetType(), out baseConverters))
        {
            OneWayTypeConverterBase baseConverter;

            if (baseConverters.TryGetValue(typeof(TTarget), out baseConverter))
            {
                OneWayTypeConverter<TSource, TTarget> converter = baseConverter as OneWayTypeConverter<TSource, TTarget>;

                if (converter != null)
                {
                    return converter.Convert(sourceObject);
                }
            }

            throw new InvalidOperationException("No converter found for that target type");
        }
        else
        {
            throw new InvalidOperationException("No converters found for that source type");
        }
    }
}

现在,您可以这样设置:

var converter = new TypeConverter(new List<OneWayTypeConverterBase>
{
    new OneWayTypeConverter<int, string>(x => $"The number was {x}"),
    new OneWayTypeConverter<int, bool>(x => x != 0),
    new OneWayTypeConverter<bool, string>(x => $"The bool was {x}")
});

然后只要你需要它,就可以像这样使用它:

var result = converter.ConvertType<int, string>(4);