从其中的一个Func<>中访问包含它的Dictionary<X, Func<Y>>

5

这是一个有点奇怪的问题。

我正在构建一个Web应用程序,并且正在组合一个类似于工厂模式的东西来处理一些对象实例化。我不喜欢笨重的switch-case语句,所以我倾向于这样做:

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

然后,switch块变成了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,表示“我此刻正在执行的对象”,而不是“编写时的包含对象”?

编辑: 最初,该Dictionary是静态的,所以我提到的在初始化之前使用它的错误不会发生。实际上,static实际上是从粘贴较大代码块并对其进行编辑,然后复制粘贴错误的结果。

因此,主要回答是关于创建一个单独的对象来存储Func,然后将其添加到Dictionary两次。这很有道理,也更或多或少是我最终采取的方法(由于各种原因,它还使周围的代码更加清洁),但它有点回避了我最感兴趣的部分,即在C#中是否可以在运行时访问你所在的对象,以便对象可以访问它们当前所在的字典(或列表、数组等)。

3个回答

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就足以解决问题。初始化器不一定是你想要存储的字典。

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]() }
};

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

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

然后你需要问自己,我需要它是 “静态的” 吗?我可以注入 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正在创建这些类型,因此您可以将它们注入到它们的构造函数中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接