c#, почему я не могу перейти к базовому классу моего плагина?

У меня есть решение, которое создает библиотеки DLL и другое, которое их потребляет. Это панель инструментов, которая может загружать различные инструменты в виде плагинов. Сначала все идет хорошо:

Это два класса, оба в отдельных файлах cs:

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;   }

    }
}

и вот как Toolbox создает экземпляр в событии selectionchanged списка, который содержит найденные библиотеки DLL. Он создается нормально, и часы тикают..:

  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
     }

Но приведение к классу PI_base создает недопустимое исключение приведения во время выполнения. Это говорит

'Объект типа "PIClasses.PIC_Clock" не может быть приведен к типу "PIClasses.PI_base".'

Хорошо, но почему и как я могу сделать это правильно. Я раздражен. Или слепой. Или немного тупой. Или любой из вышеперечисленных ;-)


Изменить:

Хорошо, ребята, это имеет смысл - спасибо, Скотт, за то, что так ясно изложили это.

Я согласился с вашим вторым предложением и создал специальный проект PluginCore для базового класса.

Однако я все еще столкнулся с проблемой ...: я сделал PluginCore библиотекой классов (PI_Base) и сгенерировал из нее DLL (PI_Base.DLL).

Я удалил все ссылки на исходный базовый класс из проекта ClockPlugin и добавил ссылку на PI_Base.DLL. Я также добавил предложение using в пространство имен PI_Base. И я удалил исходную ссылку на базовый класс из цели csproj. Недавно созданная ссылка на базовую DLL выглядит нормально. (?)

Но я получаю сообщение об ошибке «Тип или пространство имен не найдено» при сборке. Что странно, так как я могу щелкнуть правой кнопкой мыши тип базового класса и сказать «перейти к определению», и он выводит то, что находит в метаданных! Но при создании DLL ClockPlugin он говорит, что ни пространство имен (PI_Base), ни базовый тип (PI_ToolBase) не найдены.

Я думаю, что упускаю что-то маленькое, но важное ..

Вот соответствующие части файла csproj:

<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>

А вот часть PIC_Clock.cs, где сборка не удалась:

using PI_Base;


namespace PIClasses
{
    public class PIC_Clock : PI_ToolBase

Изменить 2

Действительно, что-то существенное отсутствовало в команде CSC. Вызов его компилятора полностью отделен от внутренней сборки Studio, и ему нужно указать, какие классы включать, а также на какие ссылаться. Я должен сослаться на DLL базового класса, если я хочу поделиться ею с другой программой, например так:

<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" />

person TaW    schedule 05.03.2014    source источник
comment
Судя по информации в других ваших вопросах, похоже, что вы скомпилировали базовый класс в каждую сборку плагина. Вам нужно, чтобы каждая сборка подключаемого модуля ссылалась на одну сборку, содержащую базовый класс.   -  person Mike Zboray    schedule 05.03.2014
comment
Вы должны убедиться, что PI_base находится в одной сборке, и что все остальные классы (производные от него) используют эту сборку.   -  person Peter Ritchie    schedule 05.03.2014


Ответы (1)


Все плагины должны иметь общий базовый класс. Если у вас есть копия 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 без нарушения работы плагинов.

person Scott Chamberlain    schedule 05.03.2014