C#为什么我不能上溯造型到我的插件的基类?我的、插件、造型

由网友(风华)分享简介:我有一个解决方案,它创建DLL和另一个消耗他们。它是一个工具箱其中可以加载各种工具作为插件。起初一切顺利的话:这些是两个阶级,无论是在单独的CS文件:命名空间PIClasses{公共类PI_base:用户控件{公共PI_base(){}公共字符串描述{获得;组; }公共字符串版本{获得;组; }私人无效的Ini​​t...

我有一个解决方案,它创建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; //例如什么样的基类可能提供

    打破; //完成后,我们发现真实的东西
     }
 
用VS.NET 未能加载基类 不能进行设计

但剧组到类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 ),或做

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

PluginCore
 |  -  PI_base.cs

ClockPlugin
 | -REFRENCES
 | L-- PluginCore
 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_BasebinDebugPI_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#13toolboxpi_basepi_basebindebugpi_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.

阅读全文

相关推荐

最新文章