【问题标题】:Is direct reference really necessary?直接引用真的有必要吗?
【发布时间】:2015-11-03 21:04:54
【问题描述】:

假设我们的 Visual Studio 解决方案中有 3 个项目。项目 A、B 和 C。

项目A代码:

public class A
{
  public void someMethodA()
  {
    B b = new B();
    b.someMethodB(new C());
  }
}

项目B代码:

public class B
{
  public void someMethodB(C c)
  {
    c.foo();  // we do something with C, call C's method
  }
}

项目C代码:

public class C
{
  public void foo()
  {
    return; // just for testing
  }
}

项目 B 有一个对项目 C 的引用,因为它需要它(在 someMethodB 中需要类 C)。项目 A 在创建新对象 B 并调用 B 的方法 - someMethodB() 时引用了项目 B。

此时我们的解决方案将无法编译,因为项目 A 需要引用项目 C - 它调用需要项目 C 的 b.someMethodB(new C())。项目 A 真的需要引用项目 C 吗?它不能从也引用它的项目 B 中获取它。我知道每个项目都会生成自己的 dll 文件,并且在 bin 目录中有 3 个文件,但它仍然可以从项目 B 中获取所需的内容。

项目 C 不是 B 域,但我们可以通过 B 获得对 C 的引用。

【问题讨论】:

  • "我们可以通过 B 得到对 C 的引用",没错,你可以通过汇编 B 得到一个 C 的实例(虽然你不会可以用它做很多事情,除了将它传递给程序集B 的其他部分。但是您正尝试使用new 关键字在程序集A实例化它。
  • 根据我的经验,即使在 C# 编译器不需要它的情况下,添加直接引用也是一个好主意。虽然这很丑陋,但 .net 构建系统有时不会重建它需要重建的东西。
  • @CodesInChaos 这是否与有时删除 bin/ 和 obj/ 目录有关。有时会发生异常,当我们删除 bin/ 和 /obj 目录时一切正常(当然删除目录后需要重建)
  • @Groo 我只是认为程序集 B 可以在没有 A 引用 C 的情况下返回 C(以及所有 C 的元数据和所需的任何信息)。我只是尝试 B 返回新的 C(),但 A 仍然需要一个参考 C,因为 A 需要知道如何使用 C。我相信 James Thorpe 的回答有一定的道理。
  • @broadband:它需要引用,因为它需要知道C 类型有哪些成员。 但是,如果三个程序集都引用了一个定义了某个接口的通用程序集(例如IC),那么A可以通过接口IC获取C的实例,只要因为C 实现IC 并且B 方法的返回类型是IC 而不是C。这是我的想法,但应该更明确。

标签: .net reference


【解决方案1】:

如果 A 只是调用 B 上的方法而无需传入 C,即使该方法本身使用 C,您也不需要直接引用。但是,由于 A 需要构造 C,因此它需要直接引用 C 才能获得所需的元数据 - 此元数据包含在 C 中,不会复制到 B。

如果所有元数据都是从链上所有引用的库中复制的,那么您只需要直接引用您正在使用的 dll,那么即使从未使用过该功能,dll 的大小也会膨胀 - 它需要“以防万一”。

【讨论】:

    【解决方案2】:

    为什么不在项目 B 中创建一个返回 new C() 的方法?那么你可以避免在A中使用C。

    【讨论】:

    • 刚试过。它说我仍然需要添加对 C 的引用(来自项目 A)。 B的方法:public void someMethodB() { return new C(); }
    【解决方案3】:

    是的,A 需要直接引用,因为它需要知道它正在处理什么样的C
    例如,可能有一个System.Whatever.C 和一个MyProject.C - 现在如果你不指定它应该在new C() 调用什么构造函数?

    如果您想避免 A 中的引用,您可以在 someMethodB 中创建 C 的实例,而不是将其作为参数传递。

    【讨论】:

    • 我们有 ConnectionInfo 类,它包含有关 sql 数据库的信息,例如用户名、模式、密码、数据库……接下来,我们创建例如 UserService 项目,该项目使用此 ConnectionInfo 类从数据库中读取用户。最后我们有一些网络服务,例如UsersWebService 使用 UserService 来获取/搜索用户。这就是我想到这个问题的故事。
    【解决方案4】:

    如果您的C 程序集实现了某个接口(例如IC),并且程序集A 引用了定义了接口 的程序集,那么A 将不必了解C,只要B 方法的返回类型不是具体的C,而是接口IC

    从示意图上看,应该是这样的:

    无论如何,这很可能是您组织应用程序的方式。我注意到您提到传递某个 ConnectionInfo,其中包含数据库连接所需的信息,但您当然不想从 UI 层访问数据库凭据,甚至无法创建实际的 @987654333 @ 实例。

    例如,您可能会实现一种存储库模式,或者像这样的简单 ORM:

    // IC assembly (common assembly containing entities and repo interfaces)
    public interface ISession : IDisposable
    {
         // It can even be an empty, marker interface for the
         // outside world. Only `C` will know its internals anyway.
    }
    
    public interface IRepoFactory
    {
         ISession OpenSession();
         IRepo<T> GetRepo<T>(ISession session);
    }
    

    您的B 程序集可以将具体的Session 实例返回给A

    // B assembly
    // RepoFactory should also be internal to B assembly,
    // the only thing that A needs is a method which will
    // provide the implementation of IRepoFactory
    internal RepoFactory : IRepoFactory
    {
        public ISession OpenSession()
        {
            // this creates a concrete instance,
            // but callers will only get the `ISession` interface
            return C.CreateActualSession();
        }
    
        // other methods should also accept the session
        // object through its interface
        public IRepo<T> GetRepo<T>(ISession session)
        {
            return C.GetRepo(session);
        }
    }
    

    但是您的A 程序集可以使用ISession,却不知道它是如何在后台实现的:

    // somewhere in `A` assembly
    var repoFactory = B.GetRepoFactory();
    using (var session = repoFactory.OpenSession())
    {
        var ordersRepo = repoFactory.GetRepo<IOrdersRepo>(session);
        ordersRepo.Save(something);
    }
    

    【讨论】:

    • 我明白你的意思,共享相同的接口/合同。一个很好的示意图方法。 B 程序集 public RepoFactory 不应该继承 IRepoFactory 接口,例如公共回购工厂:回购工厂
    • 我相信正确的是作为 UI 层的 UserWebService 不应该知道数据库凭据,是的。但是数据库连接字符串是从调用者的 web.config 文件中读取的。在这种情况下,调用者是 UI 层 - Userwebservice 或 winforms。也许更好的方法是在 UserService 项目中创建 app.config 文件?显然微软有这种模式。 In 从调用者配置文件中读取所有数据。我不知道什么更好。因此,基本上使用 Microsoft 模式会强制您在 UI 应用程序中保留所有必要的设置。
    • 只是想补充一点,我们从 web.config 调用者文件中定义的设置创建 ConnectionInfo 对象。
    • @broadband:您应该让您的数据层使用自己的设置,而不是要求 UI 加载它们并转发它们。但是,如果您使用 .NET 应用程序设置(app.config 或 web.config),则在运行时读取的实际值将始终从入口点的设置文件中读取。换句话说,您可以将app.config 添加到您的数据层,但取决于您是从Web 项目还是从winforms 应用程序启动该过程,将从不同的位置读取设置(发布文件夹中的appname.exe.config) .但这并不意味着 UI 需要实际加载它们。
    • @broadband:您还可以查看this thread,以更好地解释配置文件在 .NET 中的工作方式。我们公司不使用 .NET 设置,而是每个程序集将其自己的设置存储在一个单独的文件中——这意味着程序集设置被限制在一个文件中,但这也意味着每个入口点必须有自己的程序集副本和在我们的例子中他们的设置。另一方面,使用 .NET,您需要将设置合并到每个入口点的单个文件中,但您可以轻松地将多个入口点保留在单个发布文件夹中。
    猜你喜欢
    • 2011-01-11
    • 2018-04-05
    • 2021-01-25
    • 1970-01-01
    • 2010-10-18
    • 2017-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多