问题
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" />
回答1:
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.
来源:https://stackoverflow.com/questions/22205198/c-sharp-why-cant-i-upcast-to-my-plug-ins-base-class