字典<x,func <y =“”>&gt;从Func&lt;&gt;访问包含的词典在其中</x,>

时间:2014-02-23 12:18:03

标签: c# dictionary this

这有点奇怪。

我正在构建一个Web应用程序,我正在整理一个简单的工厂模式,以处理一些对象实例化。我不喜欢丑陋的开关盒,所以我倾向于这样做:

public enum Option
{
    Option1,
    Option2,
    Option3
}

public Dictionary<Option, Func<MyBaseClass>> OptionMapping 
   = new Dictionary<Option, Func<MyBaseClass>>
{
    { Option1, () => new DerivedClassOne() },
    { Option2, () => new DerivedClassTwo() },
    { Option3, () => new DerivedClassThree() }
};

然后切换块变为MyBaseClass selectedThing = OptionMapping[currentOption]();

在编写此代码时,我发现如果您遇到两个选项需要相同操作的情况,那么您最终会在两个位置使用相同的Func<>,我宁愿避免这种情况。假设目前你不能只重构一些逻辑来将这两个选项合并为一个,我想知道是否有办法让一个Func调用另一个。

我的第一个想法是:

public Dictionary<Option, Func<MyBaseClass>> OptionMapping 
   = new Dictionary<Option, Func<MyBaseClass>>
{
    { Option1, () => new DerivedClassOne() },
    { Option2, () => new DerivedClassTwo() },
    { Option3, () => this[Option1]() }
};

但LINQPad中的快速测试告诉我,这不起作用,因为this当然是指编写代码的类,而不是Dictionary(显然我也花了在this更加变幻无常的语言中花费大量时间。我的第二个想法是将其更改为() => OptionMapping [Option1](),但这会导致您在初始化之前尝试使用OptionMapping的错误。你当然可以这样做:

public Dictionary<Option, Func<MyBaseClass>> OptionMapping 
   = new Dictionary<Option, Func<MyBaseClass>>
{
    { Option1, () => new DerivedClassOne() },
    { Option2, () => new DerivedClassTwo() }
};
OptionMapping.Add(Option3, () => OptionMapping [Option1]());

哪个有效,但不是很漂亮。所以,这个问题主要是好奇心。

有没有办法从另一个元素访问Dictionary的一个元素?更一般地说,C#是否有一个像this这样的关键字,意思是“我现在在里面的对象在执行时”,而不是“写入时的包含对象”? / p>

修改 最初,Dictionary是静态的,所以我提到的在初始化之前使用它的错误不会发生。 static实际上是从更大的代码块中粘贴并编辑它,然后复制粘贴错误的剩余部分。

因此,发布的主要答案是有一个单独的对象来存储Func,然后将其添加到Dictionary两次。这是有道理的,并且或多或少与我最终的结果(由于各种原因,它也使一些周围的代码更清洁)但它有点避免我最感兴趣的问题的一部分,是否可以在C#中访问您在运行时所在的对象,以便对象可以访问它们当前所在的字典(或列表或数组等)。

3 个答案:

答案 0 :(得分:2)

对于一般情况,没有你所描述的那样存在。但是,对于您的特定情况,您的词典存储在static字段中,因此您应该可以通过该字段访问它:

public static Dictionary<Option, Func<MyBaseClass>> OptionMapping 
   = new Dictionary<Option, Func<MyBaseClass>>
{
    { Option1, () => new DerivedClassOne() },
    { Option2, () => new DerivedClassTwo() },
    { Option3, () => OptionMapping[Option1]() }
};

在问题中,你说:

  

我的第二个想法是将其更改为() => OptionMapping [Option1](),但这会产生一个错误,即您在初始化之前尝试使用OptionMapping。

但那不对。你不会得到那个错误,不是在这种情况下。你可以,如果它是一个局部变量,但在这种情况下,你仍然可以这样做:

Dictionary<Option, Func<MyBaseClass>> OptionMapping = null;
OptionMapping = new Dictionary<Option, Func<MyBaseClass>>
{
    { Option1, () => new DerivedClassOne() },
    { Option2, () => new DerivedClassTwo() },
    { Option3, () => OptionMapping[Option1]() }
};

在这种情况下OptionMapping初始化的事实足以避免这个问题。初始化者不必是您要存储的词典。

答案 1 :(得分:2)

这应该有效:

private static Dictionary<Option, Func<MyBaseClass>> OptionMapping 
   = new Dictionary<Option, Func<MyBaseClass>> {
    { Option.Option1, () => new DerivedClassOne() },
    { Option.Option2, () => new DerivedClassTwo() },
    { Option.Option3, () => OptionMapping[Option.Option2]() }
};

Demo on ideone.

此方法的问题是Option3包含不同的 Func对象,每次调用它时,它依赖于另一个Func来生成其输出

我认为这不如显式创建共享Func对象的解决方案那么好,并且可以多次将它放在字典中(即您的第二个代码段或下面的替代方案)

private static Dictionary<Option, Func<MyBaseClass>> OptionMapping;
static MyClass() {
    Func<MyBaseClass> makeDerivedTwo = () => new DerivedClassTwo();
    OptionMapping = new Dictionary<Option, Func<MyBaseClass>> {
        { Option.Option1, () => new DerivedClassOne() },
        { Option.Option2, makeDerivedTwo },
        { Option.Option3, makeDerivedTwo }
    };
}

答案 2 :(得分:1)

我会尽量减少对静态的使用:

创建一个对象来保存此映射。它还可以执行实际实例化类的函数,即实际执行Func。从外部调用者不知道如何执行映射,允许您稍后使用DI容器或其他构造方法替换它:我还添加了一个界面来帮助您了解如何添加其他实现并注入它们在运行时。这对测试也很有用。

public interface IOptionFactory
{
    MyBaseClass Create(Option option);
}

public sealed class OptionFactory : IOptionFactory
{
    private readonly Dictionary<Option, Func<MyBaseClass>> _optionMapping;

    public OptionFactory()
    {
        Func<MyBaseClass> makeDerivedTwo = () => new DerivedClassTwo();

        _optionMapping = new Dictionary<Option, Func<MyBaseClass>>
        {
            { Option.Option1, () => new DerivedClassOne() },
            { Option.Option2, makeDerivedTwo },
            { Option.Option3, makeDerivedTwo }
        };
    }

    public MyBaseClass Create(Option option)
    {
        return _optionMapping[option]();
    }
}

然后你只需要一个静态来保持其他类的干净:

private static IOptionFactory OptionMapping = new OptionFactory();

然后你需要问问自己,我需要它static吗?我可以注入IOptionFactory吗?

以下是使用Unity依赖注入时可能会看到的IOptionFactory实现的示例。

public sealed class UnityOptionFactory : IOptionFactory
{
    private readonly IUnityContainer _container;

    private readonly Dictionary<Option, Type> _optionMapping;

    public UnityOptionFactory(IUnityContainer container)
    {            
        var makeDerivedTwo = typeof(DerivedClassTwo);

        _optionMapping = new Dictionary<Option, Type>
        {
            { Option.Option1, typeof(DerivedClassOne) },
            { Option.Option2, makeDerivedTwo },
            { Option.Option3, makeDerivedTwo }
        };
    }

    public MyBaseClass Create(Option option)
    {
        return _container.Resolve(_optionMapping[option]);
    }
}

优势,Unity正在创建这些类型,因此您可以注入其构造函数。