2

我一直在玩松耦合我的数据访问层。我发现依赖注入过程很有帮助,但在考虑使用泛型时遇到了一些难题。

有没有办法让一个支持泛型类型参数的类真正解耦和类型安全?我担心的是,即使您写入一个公共接口,如果派生类型与您最初编码的实体不同,那么您可能会遇到一些编译器可能无法捕获的令人讨厌的运行时错误。

问题是,当我编写代码以从数据库中获取数据并为我的对象添加水合物时,我在尝试在下一层上实现它时遇到了一个难题。如果我传入下面的 Foo2 之类的类,我可以破坏我的代码,因为没有“隐式”转换。我一直在使用反射来尝试放松,但我不断回到从数据库中获取数据时需要对具体类型进行水合的问题。然后,该类型会在转换和类型保证方面产生问题。此外,如果我想为我的实体库中的所有许多类型抽象所有方法,我可以通过反射来做到这一点,但我仍然遇到泛型只能对显式“where T: ISomeInterface”语句做出类型保证的问题. 最后,如果我们有更多的派生类型或者从接口分支形成新类型的类型,这个模型就会崩溃。认为我需要为曾经做过的每一种类型实现新的数据访问对象会迫使数据访问层发生变化。这打破了我对松散耦合的定义。

所有这一切似乎迫使我回到这个问题——泛型可以以真正松耦合的方式使用吗?如果是这样,您可以提供哪些参考资料?

简化示例:

public interface IEntity
{
    // stuff for state and methods ...
}
public interface IRepository<T> where T : IEntity
{
    void Save(out int id, T obj);
    T Load(int id);
}
public interface IFoo : IEntity
{
    int Id { get; set; }
    //All other IEntities goodness
    //Some new goodness specific to Foo
}
//Concrete Entities:
public class Foo : IFoo
{
    // blah blah
}
public class Foo2 : IFoo
{
    // new blah blah
}

public class FooRepository : IRepository<Foo> //OOPS, Looks like we have settled in on a concrete type!
{

    public void Save(out int id, Foo obj)
    {
        // ADO.NET code to access Sql ...
        id = 1;// this would actually be the result of the Sql insert output parameter
        return;
    }

    public Foo Load(int id)
    {
        Foo foo = new Foo();
        // ADO.Net code to access Sql
        return foo;
    }
}

那么,您将如何处理可以处理任何 IFoo 派生对象并且仍然能够返回完全形成的具体类型的代码,而不管程序员在路上做什么?最好放弃通用部分并坚持依赖注入吗?任何参考资料、指导等都会很棒。

4

3 回答 3

1

对于您在此处遇到的问题,您始终可以使用typeof(或者可能是不同的运算符/方法)来获取对象的动态类型。这样,您始终可以在遇到一些转换错误之前执行检查。

您可以将具体类型限制设置为顶级类型,但这确实违背了泛型的目的。

否则,我真的不知道如何克服你的问题。如果您使用泛型,重点是添加松散耦合和与类型无关的泛型操作。你可以检索它的唯一方法是你 typeof 然后重铸,捕捉边缘情况以防止铸造错误。关于你的问题:

可以以真正松耦合的方式使用泛型吗?

我不太明白你的意思?从你的问题的语义上,你当然可以。这实际上是泛型的目的之一。

那么,您将如何处理可以处理任何 IFoo 派生对象并且仍然能够返回完全形成的具体类型的代码,而不管程序员在路上做什么?

这也是相当模糊的。我可以写:

public SomeType method(IFoo obj)
{
...
}

这仍然可以处理任何 IFoo。你不需要泛型。如果你想具体返回你传入的任何类型,那么:

public T method<T>(IFoo obj) where T:IFoo
{
    return (T)obj;
}

希望这可以帮助。LMK 如果我弄错了。

于 2013-03-12T23:45:44.610 回答
1

我不得不承认对您的难题感到有些困惑;我不确定它可能是什么。

生成具体类型的存储库显然必须知道如何创建它们。为什么不为每种类型创建一个存储库,以便每个都实现IRepository<IFoo>?这样,您可以将具体实现留给 DI 容器的配置。

例如,在 ninject 我会写一些类似于

Bind<IRepository<IFoo>>.To<Foo2Repository>()

设置它。如果您需要更细粒度的控制,我所知道的大多数容器都支持某种范围界定方式,以便绑定仅在某些条件下有效。

我是否误解了什么,或者这是否解决了您的担忧?

于 2013-03-12T23:57:47.500 回答
0

你可以定义常见的东西

class BaseFooRepository<TFoo> : IRepository<TFoo> where TFoo : IFoo

然后将更具体的代码放入

class Foo2Repository : BaseFooRepository<Foo2>
于 2013-03-12T23:59:59.573 回答