【问题标题】:What is the best way to resolve an object?解决对象的最佳方法是什么?
【发布时间】:2010-11-08 19:38:18
【问题描述】:

我编写了一个解析器,因此我可以拥有一个非常原始的 DI 框架。在框架中,如果没有指定或注册任何内容,我允许依赖解析器指定要加载的默认类型。

但是,我执行默认加载的方式让我感到疑惑。我不确定我是否以最好的方式做到这一点。

例子:

T LoadDefaultForType<T>()
{
    T _result = default(T);

    if (typeof(T) == typeof(ISomeThing)
    {
        result = new SomeThing();
    }
    ... more of the same
    else
    {
        throw new NotSupportException("Give me something I can work with!");
    }

    return _result;
}

更新

如果模块或程序集没有为接口配置具体类型,则使用它来获取给定接口的默认对象。

例如:

IoC.Resolve<ISomeThing>();

如果没有向 ISomeThing 注册任何其他内容,则需要将 SomeThing 对象返回给我。在这种情况下, LoadDefaultForType 是使用默认值的最后努力(在这种情况下,无论我的域模型是什么)。

Resolve 也可能对此有所启发:

T Resolve<T>()
{
    T _result = default(T);
    if (ContainedObjects.ContainsKey(typeof(T))
        _result = (T)ContainedObjects[typeof(T)];
    else
        _result = LoadDefaultForType<T>();
    return _result;
}

有什么想法吗?鉴于我试图允许约定优于配置的方法,是否有更好的方法来加载默认类型?

【问题讨论】:

    标签: c# dependency-injection


    【解决方案1】:

    一些建议:

    您可以创建一个属性,用于标记特定接口的默认实现类型。当您尝试解析类型时,您可以在 T 上搜索该属性并使用反射来动态实例化该类型。

    或者,您可以使用反射来搜索可用(已加载)的程序集或实现接口的具体类型。这可能是一个缓慢而昂贵的过程,因此只有在默认情况很少见的情况下才有意义。

    最后,如果您对命名约定感到满意,您可以搜索与接口同名但没有前导“I”的类。不是最好的方法,但肯定是一种可以在紧要关头工作的方法。

    【讨论】:

    • 我真的很喜欢你的属性想法。我认为这可能是要走的路。
    • 通过命名约定方法,这将与“约定/配置”很好地结合在一起。
    【解决方案2】:
    public T LoadDefaultForType<T>()
            where T : new()
        {
            T _result = new T();
    
            return _result;
        }
    

    上面的代码会是一个更好的方法,但我不确定你想要做什么,更多信息将帮助你更好地做你想要实现的任何事情。

    我建议查看Unity 以了解动态加载类型,即。依赖注入

    【讨论】:

    • @Neil 谢谢,我会修改我的问题以更好地说明。如果可以的话,我肯定会使用 DI 框架,但在这种情况下,我别无选择,只能自己动手。
    • @Neil 另外,我不能使用新的约束,因为 T 必须支持接口,这将是典型的使用。
    • where T : IInterface, new() 但这意味着 LoadDefaultType 只能支持从 IInterface 派生的类型,这可行吗?
    • @Neil 不是真的,我像这样使用 LoadDefaultForType:LoadDefaultForType 它应该返回给我一个新的东西。那有意义吗?我不是在要求某事,我是在要求某事。
    • new() 约束仅适用于类或结构。您不能实例化接口的实例。这需要 Resolve() 方法将类型 T 限制为类或结构。
    【解决方案3】:

    如果 T 可以解决,Neil 的方法是最好的(我认为它也必须在同一个程序集中?)。

    在你的类中,你可以创建一个内部“注册表”,它可以与 System.Reflection 一起使用来实例化项目,而无需巨大的 switch 语句。这可以保留您的“约定优于配置”,同时也让您保持干爽。

    编辑

    结合 LBushkin 的回答的一个方面来展示一些工作代码。 (至少,它在我的脑海中编译,并且取自我知道有效的示例......)

    public T LoadDefaultForType<T>()
    {
      try
      {
        string interfaceName = typeof(T).AssemblyQualifiedName;
    
        // Assumes that class has same name as interface, without leading I, and
        // is in ..."Classes" namespace instead of ..."Interfaces"
        string className = interfaceName.Replace("Interfaces.I", "Classes.");
    
        Type t = Type.GetType(className, true, true);
        System.Reflection.ConstructorInfo info = t.GetConstructor(Type.EmptyTypes);
    
        return (T)(info.Invoke(null));
      }
      catch
      {
        throw new NotSupportException("Give me something I can work with!");
      }
    }
    

    请注意,正如所写的那样,它不能跨越程序集边界。它可以使用基本相同的代码来完成,但是 - 您只需向 Type.GetType() 方法提供程序集限定名称。(固定 - 使用 AssemblyQualifiedName 而不是 FullName;假设接口和实现类在同一个程序集中。)

    【讨论】:

    • 如果我理解正确,Neil 的解决方案不符合要求,因为它不允许覆盖默认类型。
    • @GalacticCowboy Neil 的方法并不能真正解决我的问题。我已经更新了我的问题以更好地解释。您所描述的正是我想要做的,但我不知道有一种更好的方法来创建一个“注册表”而不做巨大的 if else 或 switch 结构。我也不确定如何使用反射来解决这个问题,但我对想法持开放态度。举个例子会很有帮助。
    【解决方案4】:

    一种方法是创建一个 AssemblyQualifiedName 对列表,其中第一个包含基本类型,第二个包含要实例化的子类型。它可以在您的 App.config 或 XML 文件中或任何其他方便的地方。在启动期间阅读此列表并将其用于填充字典以用作查找表。对于键,您可以使用基的 AssemblyQualifiedName 或相应的 Type 实例。对于该值,您可能应该考虑获取子类型的 ConstructorInfo。

    【讨论】:

    • 我同意你的观点,但那是配置,而不是约定,我的 LoadDefaultForType 方法是关于设置我的约定。我的 DI 框架中有其他机制来处理配置。公约部分(这部分)是我想知道的。
    • 约定部分将由 TryGet 失败触发。此时,您可以获取 AQN 并附加一个标准后缀,例如“Default”,然后使用 Type.GetType 进行查找,将其 ConstuctorInfo 添加到表中。如果失败,则查找基本类型,添加标识条目以避免重复所有这些工作。您需要在执行此操作时锁定表,或者至少在您需要访问它的片刻期间锁定。
    【解决方案5】:

    加载默认类型的更好方法是提供一个将类型存储在哈希表中的 add 方法。这将依赖容器与注册逻辑解耦。

    您可以稍后选择更改注册码以从某个文件或其他内容中读取类型。

    【讨论】:

    • 我的 DI 框架中也有一个 Register 方法。任何人都可以注册一个对象并将其与接口相关联,但这就是我认为框架的配置部分。当有人选择不配置接口时,我需要有一个备份计划。
    猜你喜欢
    • 2019-10-05
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-30
    • 2016-02-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多