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

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

问题描述

我有一个解决方案,它创建DLL和另一个消耗他们。它是一个工具箱其中可以加载各种工具作为插件。起初一切顺利的话:

这些是两个阶级,无论是在单独的CS文件:

 命名空间PIClasses
{
    公共类PI_base:用户控件
    {
        公共PI_base(){}

        公共字符串描述{获得;组; }
        公共字符串版本{获得;组; }

        私人无效的Ini​​tializeComponent()
        {
            this.SuspendLayout();
            this.Name =PI_base;
             this.ResumeLayout(假);
        }
    }
}


命名空间PIClasses
{
    公共类PIC_Clock:PI_base
    {
        私人System.ComponentModel.IContainer成分= NULL;
+保护覆盖无效的Dispose(BOOL处置)
+私人无效的Ini​​tializeComponent()

        公共System.Windows.Forms.Label st_time;
        公共System.Windows.Forms.Timer clockTimer;

        公共PIC_Clock(){的InitializeComponent(); }

        私人无效clockTimer_Tick(对象发件人,EventArgs的)
        {st_time.Text = DateTime.Now.ToString(HH:MM:SS); }

        私人无效PIC_Clock_Load(对象发件人,EventArgs的)
        {clockTimer.Enabled = TRUE; }

    }
}
 

和这是一个列表框,其中包含发现的DLL的SelectionChanged事件工具箱如何创建一个实例。它被创建精细和时钟滴答......

 字符串DLLNAME = lb_tools.SelectedItem.ToString(); //选择一个从DLL列表
  装配装配= Assembly.LoadFrom(DLLNAME); //加载程序集

  的foreach(在assembly.GetExportedTypes类型类型())//查找工具类
  {
     如果(type.Name!=PI_base)//跳过基类
     {
         变种C = Activator.CreateInstance(类型);
         tp_test2.Controls.Add((控制)C); //我可以补充的是一个标签页的控制
         ((控制)C).Dock = DockStyle.Fill; //这个工程,太

         // PI_base CTL =(PI_base)C; //<  - 这个转换获得运行时错误
         // PI_base CTL = C; //为PI_base; //这个投GET空
         //st_status.Text = ctl.Description; //例如什么样的基类可能提供

    打破; //完成后,我们发现真实的东西
     }
 

但剧组到类PI_base创建一个无效的转换异常的运行时间。它说

  类型

对象PIClasses.PIC_Clock不能转换为类型   PIClasses.PI_base。

确定,但我为什么,我该怎么做是正确的。我烦恼。或者失明。还是一点点愚蠢的。或任何上述的; - )


编辑:

确定的乡亲,这是有道理的 - 谢谢你斯科特拼写出来,以便明确

我去你的第二个建议,并建立了一个专门的PluginCore项目的基类。

我还是打了一个问题,但..: 我做了一个PluginCore classlibrary(PI_Base),并从它产生的DLL(PI_Base.DLL)。

我洁净的ClockPlugin项目中所有引用到原来的基类,并增加了一个参考PI_Base.DLL。 我也加入了使用条款的PI_Base命名空间。我已经删除从的csproj目标原来的基类引用。新创建的参照基础DLL看起来好像没什么问题。 (?)

不过,我得到一个类型或命名空间不能找到错误的版本。这是奇怪的,因为我可以单击鼠标右键的基类的类型,并说转到定义和它带来了东西,它发现,在元数据!但在建设ClockPlugin DLL它说,无论是命名空间(PI_Base),也不是基本类型(PI_ToolBase)被发现。

我想我失去了一些东西虽小,但必要的。

下面是的csproj文件的相关部分:

 < ItemGroup>
    <参考包括=PI_Base,版本= 1.0.0.0,文化=中性的ProcessorArchitecture = MSIL>
      < SpecificVersion>假< / SpecificVersion>
      < HintPath> .. \ .. \ PI_Base \ PI_Base \斌\调试\ PI_Base.dll< / HintPath>
    < /参考>
    <参考包括=系统/>
..
..
    <编译包括=PIC_Clock.cs>
      <亚型GT;用户控件< /亚型GT;
    < /编译>
..
..
  <目标名称=BuildPlugins>
    < CSC来源=PIC_Clock.cs的TargetType =库
    OutputAssembly =$(OutputPath)PIC_Clock.dll
    EmitDebugInformation =真/>
  < /目标>
  <目标名称=AfterBuildDependsOnTargets =BuildPlugins>
  < /目标>
  <的PropertyGroup>
    < PostBuildEvent>调用x_copy.bat
< / PostBuildEvent>
 

这里是PIC_Clock.cs的一部分,其中的构建失败:

 使用PI_Base;


命名空间PIClasses
{
    公共类PIC_Clock:PI_ToolBase
 

编辑2

确实是一件重要的是缺少从CSC命令。它的编译器的调用是从内部Stud​​io生成相当独立的,需要哪些类被告知为包含,也其中的引用。我必须引用如果我想与其他程序共享,基类的DLL,例如是这样的:

 < CSC来源=PIC_Clock.cs参考=D:\ p \ C#13 \工具箱\ pi_base \ pi_base \斌\调试\ pi_base.dll的TargetType = 库OutputAssembly =$(OutputPath)PIC_Clock.dllEmitDebugInformation =真/>
 

解决方案

该插件需要的所有共享一个基类。如果你有 PI_base.cs 的副本,每个插件也不会因为即使他们有他们仍然没有考虑到相同的名称和命名空间和完全相同的布置工作同一类。

我想这就是你的计划目前看起来如何

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

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

相反,你需要做的两个设置之一,无论是

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

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

让你的插件引用 PI_Base 在主项目(此方法容易受到插件,当您更改 MainProject <程序集的版本号突破/ code>),或做

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

PluginCore
 |  -  PI_base.cs

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

所以,现在无论你的EXE和你的插件DLL的同时读取单个 PluginCore.dll ,这将导致更多的DLL文件在您的项目,但你可以在<$更改程序集的版本号C $ C> 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_Base\PI_Base\bin\Debug\PI_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:\p\c#13\toolbox\pi_base\pi_base\bin\debug\pi_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天全站免登陆