c# 为什么我不能向上转换到插件的基类? [英] c# why can't I upcast to my plug-in's base class?

查看:14
本文介绍了c# 为什么我不能向上转换到插件的基类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个创建 DLL 的解决方案和另一个使用它们的解决方案.它是一个工具箱,可以将各种工具作为插件加载.起初一切顺利:

这是两个类,都在单独的 cs 文件中:

命名空间 PIClasses{公共类 PI_base : UserControl{公共 PI_base() { }公共字符串 描述 { 获取;放;}公共字符串版本 { 获取;放;}私有无效 InitializeComponent(){this.SuspendLayout();this.Name = "PI_base";this.ResumeLayout(false);}}}命名空间 PIClasses{公共类 PIC_Clock : PI_base{私有 System.ComponentModel.IContainer 组件 = null;+ 保护覆盖无效处置(布尔处置)+ 私有无效 InitializeComponent()公共 System.Windows.Forms.Label st_time;公共 System.Windows.Forms.Timer 时钟定时器;公共 PIC_Clock() { InitializeComponent();}private void clockTimer_Tick(object sender, EventArgs e){ st_time.Text = DateTime.Now.ToString("HH:mm:ss");}私有无效PIC_Clock_Load(对象发送者,EventArgs e){clockTimer.Enabled = true;}}}

这就是工具箱如何在列表框的 selectionchanged 事件中创建一个实例,其中包含找到的 DLL.它创建得很好,时钟滴答作响..:

 string DLLname = lb_tools.SelectedItem.ToString();//从 DLL 列表中选择一个汇编程序集 = Assembly.LoadFrom(DLLname);//加载程序集foreach (Type type in assembly.GetExportedTypes())//查找工具类{if (type.Name != "PI_base")//跳过基类{var c = Activator.CreateInstance(type);tp_test2.Controls.Add((Control)c);//我可以将 is 添加到标签页作为控件((Control)c).Dock = DockStyle.Fill;//这也有效//PI_base ctl = (PI_base)c;//<--这个转换得到一个运行时错误//PI_base ctl = c;//作为 PI_base ;//这个强制转换为空//st_status.Text = ctl.Description;//基类可能提供的示例休息;//当我们找到真实的东西时完成}

但是转换到类 PI_base 会在运行时创建一个无效的转换异常.它说

<块引用>

'类型为PIClasses.PIC_Clock"的对象不能转换为类型PIClasses.PI_base".'

好的,但是我为什么以及如何做对.我很烦或者瞎了眼.或者有点傻.或以上任何一种;-)

<小时>

好的伙计们,这是有道理的 - 感谢斯科特如此明确地拼写出来.

我采纳了你的第二个建议,并为基类创建了一个专用的 PluginCore 项目.

我还是遇到了问题..:我已将 PluginCore 设为类库 (PI_Base) 并从中生成了一个 DLL (PI_Base.DLL).

我已从 ClockPlugin 项目中清除了对原始基类的所有引用,并添加了对 PI_Base.DLL 的引用.我还在 PI_Base 命名空间中添加了一个 using 子句.我已经从 csproj 目标中删除了原始基类引用.新创建的对基本 DLL 的引用对我来说看起来不错.(?)

但我在构建时收到未找到类型或命名空间"错误.这很奇怪,因为我可以右键单击基类类型并说转到定义",它会调出它在元数据中找到的东西!但是在构建 ClockPlugin DLL 时,它说找不到命名空间 (PI_Base) 和基类型 (PI_ToolBase).

我想我错过了一些小而重要的东西..

这里是csproj文件的相关部分:

<Reference Include="PI_Base, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"><SpecificVersion>False</SpecificVersion><HintPath>....PI_BasePI_BaseinDebugPI_Base.dll</HintPath></参考><Reference Include="系统"/>....<编译包含=PIC_Clock.cs"><SubType>UserControl</SubType></编译>....<目标名称="BuildPlugins"><CSC Sources="PIC_Clock.cs" TargetType="library"OutputAssembly="$(OutputPath)PIC_Clock.dll"EmitDebugInformation="true"/></目标><目标名称="AfterBuild" DependsOnTargets="BuildPlugins"></目标><属性组><PostBuildEvent>调用 x_copy.bat</PostBuildEvent>

这是 PIC_Clock.cs 中构建失败的部分:

使用PI_Base;命名空间 PIClasses{公共类 PIC_Clock : PI_ToolBase

编辑 2

CSC 命令中确实缺少一些重要的东西.它的编译器调用与内部 Studio Build 完全分开,需要告知哪些类要包含,哪些类要引用.如果我想与另一个程序共享它,我必须引用基类 DLL,例如像这样:

<CSC Sources="PIC_Clock.cs" References="d:pc#13	oolboxpi_basepi_baseindebugpi_base.dll" TargetType="library" OutputAssembly="$(OutputPath)PIC_Clock.dll" EmitDebugInformation="true"/>

解决方案

所有插件都需要共享一个基类.如果您在每个插件中都有 PI_base.cs 的副本,那么即使它们具有相同的名称和命名空间以及完全相同的布局,它们仍然不会被视为同一类".>

我认为这就是你的程序目前的样子

主项目|-- PI_base.csL-- Main.cs时钟插件|-- PI_base.csL--PIC_Clock.cs

相反,您需要进行两种设置之一

主项目|-- PI_base.csL-- Main.cs时钟插件|-参考文献|L--主项目L--PIC_Clock.cs

以便您的插件引用主项目中的PI_Base(当您更改MainProject 的程序集版本号时,此方法容易受到插件破坏的影响),或者执行>

主项目|-参考文献|L--插件核心L-- Main.cs插件核心|-- PI_base.cs时钟插件|-参考文献|L--插件核心L--PIC_Clock.cs

所以现在你的 EXE 和你的插件 DLL 都读取了一个 PluginCore.dll,这会导致你的项目中有更多的 DLL,但你可以在 MainProject 上更改程序集版本号没有插件损坏.

I have a solution which creates DLLs and another one which consumes them. It is a Toolbox which can load various tools as plug-ins. At first things go well:

These are the two classes, both in separate cs files:

namespace PIClasses
{
    public class PI_base : UserControl
    {
        public PI_base()  { }

        public string Description { get; set; }
        public string Version { get; set; }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            this.Name = "PI_base";
             this.ResumeLayout(false);
        }
    }
}


namespace PIClasses
{
    public class PIC_Clock : PI_base
    {
        private System.ComponentModel.IContainer components = null;
+       protected override void Dispose(bool disposing)
+       private void InitializeComponent()

        public System.Windows.Forms.Label st_time;
        public System.Windows.Forms.Timer clockTimer;

        public PIC_Clock()       {  InitializeComponent();  }

        private void clockTimer_Tick(object sender, EventArgs e)
        { st_time.Text = DateTime.Now.ToString("HH:mm:ss");   }

        private void PIC_Clock_Load(object sender, EventArgs e)
        {       clockTimer.Enabled = true;   }

    }
}

and this is how the Toolbox creates an instance in the selectionchanged event of a listbox, which contains the DLLs found. It gets created fine and the clock ticks..:

  string DLLname = lb_tools.SelectedItem.ToString();    // pick one from a list of DLLs
  Assembly assembly = Assembly.LoadFrom(DLLname);       //load the assembly

  foreach (Type type in assembly.GetExportedTypes())    // look for the tool class
  {
     if (type.Name != "PI_base")                        // skip the base class
     {
         var c =  Activator.CreateInstance(type);
         tp_test2.Controls.Add((Control)c);            // I can add is to a tabpage as Control
         ((Control)c).Dock = DockStyle.Fill;           // this works, too

         //PI_base ctl = (PI_base)c;                   // <--this cast gets a runtime error
         //PI_base ctl = c; // as   PI_base ;          // this cast get null
         //st_status.Text = ctl.Description;           // example of what the base class might deliver

    break;                                        // done when we find the real thing
     }

But the cast to the class PI_base creates an invalid cast exception on runtime. It says

'Object of type "PIClasses.PIC_Clock" can't be cast to type "PIClasses.PI_base".'

OK, but why and how can I do it right. I'm vexed. Or blind. Or a tad dumb. Or any of the above ;-)


Edit:

OK folks, that makes sense - thank you Scott for spelling it out so explicitly.

I went for your second suggestion and created a dedicated PluginCore project for the base class.

I still hit a problem though..: I have made the PluginCore a classlibrary (PI_Base) and generated a DLL (PI_Base.DLL) from it.

I have purged all references to the original base class from the ClockPlugin project and added a reference to the PI_Base.DLL. I have also added a using clause to the PI_Base namespace. And I have deleted the original base class reference from the csproj target. The newly created reference to the base DLL looks fine to me. (?)

But I get a "Type or namespace not found" error on build. Which is weird, as I can rightclick the base class type and say 'goto definition' and it brings up the stuff it does find in the metadata! But on building the ClockPlugin DLL it says that neither namespace (PI_Base) nor the base type (PI_ToolBase) are found.

I guess I'm missing something small but essential..

Here are the relevant parts of the csproj file:

<ItemGroup>
    <Reference Include="PI_Base, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>....PI_BasePI_BaseinDebugPI_Base.dll</HintPath>
    </Reference>
    <Reference Include="System" />
..
..
    <Compile Include="PIC_Clock.cs">
      <SubType>UserControl</SubType>
    </Compile>
..
..
  <Target Name="BuildPlugins">
    <CSC Sources="PIC_Clock.cs" TargetType="library" 
    OutputAssembly="$(OutputPath)PIC_Clock.dll" 
    EmitDebugInformation="true" />
  </Target>
  <Target Name="AfterBuild" DependsOnTargets="BuildPlugins">
  </Target>
  <PropertyGroup>
    <PostBuildEvent>call x_copy.bat
</PostBuildEvent>

And here is the part of the PIC_Clock.cs where the build fails:

using PI_Base;


namespace PIClasses
{
    public class PIC_Clock : PI_ToolBase

Edit 2

Indeed something essential was missing from the CSC command. Its compiler call is quite separate from the internal Studio Build and needs to be told which classes to include and also which to reference. I must reference the base class DLL if I want to share it with another program, for example like this:

<CSC Sources="PIC_Clock.cs" References="d:pc#13	oolboxpi_basepi_baseindebugpi_base.dll" TargetType="library" OutputAssembly="$(OutputPath)PIC_Clock.dll" EmitDebugInformation="true" />

解决方案

The plugins need to all share a single base class. If you have a copy of PI_base.cs in each plugin it will not work as even though they have the same name and namespace and exact same layout they are still not considered the "same class".

I think this is how your program currently looks

MainProject
 |-- PI_base.cs
 L-- Main.cs

ClockPlugin
 |-- PI_base.cs
 L-- PIC_Clock.cs

Instead you need to do one of two setups, either

MainProject
 |-- PI_base.cs
 L-- Main.cs

ClockPlugin
 |-REFRENCES
 |  L-- MainProject
 L-- PIC_Clock.cs

so that your plugins reference the PI_Base in the main project (this method is vulnerable to plugins breaking when you change assembly version numbers of MainProject), or do

MainProject
 |-REFRENCES
 |  L-- PluginCore
 L-- Main.cs

PluginCore
 |-- PI_base.cs

ClockPlugin
 |-REFRENCES
 |  L-- PluginCore
 L-- PIC_Clock.cs

So now both your EXE and your plugin DLL's both read a single PluginCore.dll, this causes more DLL's in your project but you can change assembly version numbers on MainProject without plugins breaking.

这篇关于c# 为什么我不能向上转换到插件的基类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆