【问题标题】:Referencing a class that is not instantiated / does not yet exist引用未实例化/尚不存在的类
【发布时间】:2018-05-29 09:37:11
【问题描述】:

我有一个旨在与外部软件包集成的 Winforms 应用程序。该应用程序从这些包中读取数据并将其推送到我们的服务器,用户在该服务器上登录并使用我们的应用程序(App)。

public abstract ClassToImplement
{
    public abstract void DefinedMethod1();
    public abstract void DefinedMethod2();
}

当我们设计应用程序时,它打算完成 95% 的集成工作,其余 5%(实现类/App2)由熟悉第 3 方软件的顾问开发。

public class Implemented : ClassToImplement{
    public override void DefinedMethod1(...);
    public override void DefinedMethod2(...);
}

“App”输出一个类库,然后在实现 (App2) 中引用该类库。在我们的设计中,我们创建了一个抽象类并定义了方法。这个想法是顾问将下载实现类的 repo 并将应用程序作为参考。然后他们会为他们正在实现的方法编写必要的代码,编译并“瞧!”

出于显而易见的原因,我不想与外部开发人员共享源项目,否则我只会共享完整的解决方案并使用单个应用程序,虽然我知道他们可以通过 DLL 参考看到很多东西,我们更容易控制一切。

App自带问题:主应用算法需要实例化实现类,然后程序才能完美运行。

在 App 的 Form1.cs 中:

ClassToImplement impObj = new Implemented();
impObj.DefinedMethod1();
impObj.DefinedMethod2();

我面临的挑战是我无法在不实例化类的情况下构建“应用程序”来输出 DLL。我无法实例化实现类,因为我还没有代码(还)。

很高兴知道如何通过依赖(尚未)编写的代码来实现这种抽象,以及我正在尝试做的技术术语是什么?

【问题讨论】:

  • 用接口代替抽象类怎么样?
  • @thehennyy 我不反对这样做,这将如何解决实例化问题?您能否使用我的代码示例分享帮助资源或答案?我很高兴尝试不同的方法
  • @MineR 您是否建议 App2 然后以某种方式“覆盖”空白实现?您是否有涵盖此内容的示例或文章,以便我了解该方法?
  • @Daniel 我误解了这个问题。

标签: c# winforms oop abstract-class


【解决方案1】:

为了让它“工作”,使用一个返回抽象类实例的 Func。

在您的秘密仓库中:

//Your "App" DLL Project
public abstract class ClassToImplement
{
    public abstract void DefinedMethod1();
    public abstract void DefinedMethod2();
}


public class App : Form
{

    public App(Func<ClassToImplement> initiator)
    {
        InitializeComponent();

        ClassToImplement ci = initiator.Invoke();
        ci.DefinedMethod1();
        ci.DefinedMethod2();
    }
}


//This is in a separate project which will be your startup project internally

public class Dummy : ClassToImplement
{
    public override void DefinedMethod1(){}
    public override void DefinedMethod2(){}
}

public class Program
{
    public static void Main()
    {
        Application.Run(new App(()=> new Dummy()));
    }
}

在与顾问共享的 repo 中:

// In the repo which is shared with the consultant
// This will be the startup project on the build server, and when the consultant is testing.
public class Implementation : ClassToImplement
{
    public override void DefinedMethod1(){}
    public override void DefinedMethod2(){}
}

public class Program
{
    public static void Main()
    {
        Application.Run(new App(()=> new Implementation()));
    }
}

在您的构建服务器上,您可以从两个存储库中提取,并将启动项目设置为提供给顾问的项目。但是,当您在内部进行测试和开发时,您将启动项目设置为您的版本,并且实现什么都不做。

附带说明,如果您认为您正在做的事情需要受到签署保密协议的顾问的保护,请确保在发布时进行混淆。

【讨论】:

  • 这种方法看起来不错,只是在重构以实现此建议后,我收到错误“不可调用的成员'实现'不能像方法一样使用。”有什么想法我哪里出错了吗?
  • 这是一个语法错误 - 如果您发布代码,就会清楚。
  • 你是对的,我的错,谢谢,它似乎工作正常,只是运行测试
【解决方案2】:

这通常是一个两步过程:

  1. 找到并加载程序集/dll:

    Assembly assembly = Assembly.LoadFrom(DLL);
    
  2. 实例化实现的类:

    Type type = assembly.GetType(FullNameOfImplemented);
    AppInstance = (ClassToImplement)Activator.CreateInstance(type, parameters);
    

【讨论】:

  • 谢谢你,阿米特。在其他答案中,我成功地格式化了代码。我很沮丧很长时间尝试和失败。
【解决方案3】:

您要查找的进程通常称为stubbing。在这种情况下,您选择将集成功能封装在库中,而不是 Web 服务中,但原理是一样的。

这个想法是顾问会下载实现类的 repo 并包含应用程序作为参考。

这听起来像是你搞错了依赖关系。如果顾问的代码引用了你的应用,那么你的应用就不能引用它——这将是一个循环依赖。相反,您的应用应该更符合以下条件:

App | | App.Integration.Contracts ^ ^ | | | App.Integration.Stub | App.Integration

抽象类——它可以很容易地成为 C# 中的接口——驻留在 Contracts 程序集中。这是您的应用程序具有的唯一编译依赖项。然后在运行时使用配置来加载存根或使用 IoC 容器的完整实现。一个例子是Unity,你需要它的configuration API。引用要在配置文件中使用的真实类型并仅更改以更新您的应用程序以使用全部功能。

【讨论】:

  • 感谢@Tom,这听起来符合我们正在努力实现的目标,您关于循环依赖的观点非常正确。考虑到您提到的所有内容,如果主要逻辑驻留在应用程序中,并且所需的方法来自 App.Integration.Stub,而这些方法是从 App.Integration 继承的,我将如何实例化以构建顾问需要的 DLL ?这是 Unity 框架会做的事情吗?原谅我的无知,我不习惯这种抽象层次
  • 是的,实例化依赖项是 IoC 容器的责任。理想情况下,您将使ClassToImplement 成为表单的构造函数依赖项或表单的依赖项,并且假设类型已注册,IoC 容器将隐式理解它必须实例化派生类型。在推送时,您可以将容器包装在调用Resolve&lt;T&gt;() 的静态类中,但这被称为service locator,有些人不喜欢它,但如果您了解它的局限性,它也不是本身。
【解决方案4】:

首先,如果您不想与其他开发人员共享代码,我认为您需要实现一个适当的插件系统。

其次,您应该针对您的接口而不是针对其实现进行编码。首先是因为您没有它,其次是因为您可能想要为不同的 3rd 方软件切换实现。

如果您需要一个用于测试或其他东西的实例,您可以使用手写模拟或模拟框架。如果您稍后需要一个真实的实例(当其他开发人员交付时),您可以使用一些设计模式,如工厂模式或其他设计模式来创建。如果您想稍后更改实现,请尽量避免使用 new 关键字。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-25
    相关资源
    最近更新 更多