基本クラス(UserControl)から継承し、インターフェイス(IFrameworkClient)を実装するクラス(TabControlH60)があります。 .NETActivatorクラスを使用してオブジェクトをインスタンス化します。返されたインスタンスを使用して、UserControl基本クラスにキャストできますが、インターフェイスにはキャストできません。私が得る例外は、コードスニペットの下です。インターフェイスにキャストするにはどうすればよいですか?
object obj = Activator.CreateInstance(objType);
Type[] interfaces = obj.GetType().GetInterfaces(); // contains IFrameworkClient
m_Client = (UserControl)obj; // base class cast works
IFrameworkClient fc = (IFrameworkClient)obj; // interface cast fails
// Note: The (IFrameworkClient)obj cast works fine in the debugger Watch window.
{"Unable to cast object of type 'FPG.H60.AFF.TabControlH60' to type
'FPG.AFF.Interfaces.IFrameworkClient'."}
私のライブラリが「プラグイン」機能を提供しているのと同じ問題が嫌いです...ついに機能しました...
ここに私の問題がありました:プラグインを使用する1つのメインアセンブリ、プラグインを使用する1つのアセンブリ(Plugin.dll)、およびプラグイン機能を提供する(重要な)別のアセンブリ(Library.dll)がありました。
Plugin.dllは、メインアセンブリ(拡張できるようにするため)とLibrary.dllをplugin-funcで参照していました。 -バイナリは、メインアセンブリに関連するディレクトリ「./Plugins」に移動します。
メインアセンブリはplugin-funcも参照していました。 「PluginManager」を使用するためのアセンブリが書き込まれます。この「PluginManager」はパスを取得し、リフレクションを介してすべての* .dllファイルをロードして、「IPlugin」インターフェイス(Library.dllからも取得)があるかどうかを分析します。
プラグインをロードするためにPluginManagerを呼び出すたびに、プラグインは実装されていても「IPlugin」にキャストできませんでした。
私はほとんど怒った-しかしそれから私は全体の問題を見つけた。プラグインをコンパイルすることにより、「Plugin.dll」だけでなく「Library.dll」が「./Plugins」ディレクトリに書き込まれました。 PluginManagerで毎回誤って「Library.dll」をロードすることにより、2種類の「IPlugin」ができました。1つはメインアセンブリから使用される実際の「Library.dll」にあり、もう1つはPluginManagerを介してロードされます。それらは互換性がありませんでした!
注意-「./ Plugins/Library.dll」をロードしないと、それでも問題が発生します-「Library.dll」を参照する「Plugin.dll」をロードすると、同じディレクトリにあるものだけが使用されるためです。 .. TILT ... !!私のPluginManagerは、「Library.dll」を見つけた場所から削除するだけです。
手がかりは次のとおりです。異なるコンテキストで2つのアセンブリにアクセスしないようにしてください。
ここで最も可能性の高い原因は、IFrameworkClient
が2つのケースで異なるアセンブリからのものであり、したがって異なる.NETタイプであるということです。同じコードであっても、タイプが異なる場合があります。
AssemblyQualifiedName
を確認してください。このアセンブリをリフレクションでロードしている場合、load-contextのおかげで、異なるタイプを取得できることにも注意してください同じAssemblyQualifiedNameでも。
Interface
が別のアセンブリ内で、クラスを取得した場合動的別のアセンブリのrun-time
で、interface casting
はサンプルのように失敗します(C#は、クラスが継承したものとは異なるタイプとしてインターフェイスを認識します)。
これはこの場合の私のシンプルで便利なテクニックです:
私のClass
が前述のInterface
(eq。IFrameworkClient
)から継承していると確信できる場合は、次のように記述します1つの魔法のコード行この:
dynamic fc = obj as IFrameworkClient ?? (dynamic) obj;
このテクニックを使用すると、次のことができます:
design time
情報とvsエディターインテリジェンスシステムに基づいてInterface members
のfc
のこのコード行の後にコードを記述します。run-time
でのインターフェイスキャストエラーを防止します注:
dynamic
タイプを使用するには、C# v4
が必要ですdynamic
タイプを使用するのは好きではありませんが、このような場合に役立ちます。独立したプロジェクト(クラスライブラリ)の独立した名前空間(名前空間が必要)でIFrameworkClientインターフェイスを定義します。次に、クラスライブラリの参照をコントロールプロジェクトとメインプロジェクトに追加します。
何かがあなたのサンプルコードがいくつかのものを省略していることを教えてくれます...
_class Program
{
static void Main(string[] args)
{
var type = typeof(MyClass);
object obj = Activator.CreateInstance(type);
Type[] interfaces = obj.GetType().GetInterfaces();
var m_Client = (UserControl)obj;
IFrameworkClient fc = (IFrameworkClient)obj;
}
}
public interface IFrameworkClient { }
public class UserControl { }
public class MyClass : UserControl, IFrameworkClient { }
_
これはコンパイルされて実行されます。
IFrameworkClientの定義を含むDLLは、キャストを試みる前にまだロードされていません。これは、Activator.CreateInstanceを使用している場合に発生する可能性があります。
キャストの前にvar forceLoad = typeof(IFrameworkClient);
を挿入してみてください。
私の場合、インスタンスを作成して実行時にインターフェイスタイプに割り当てていたため、必要なDLLをコピーするためにビルドイベントを追加する必要がありました。それ以外の場合はDLLロードされたDLLは最新のDLLではない可能性があるため、インターフェイスにキャストされない可能性があります。
この場合(DLLを参照として追加する代わりに)ビルドイベントを使用した理由は、メインアプリケーションがインターフェイスタイプのみを参照し、その他はすべて参照する必要があるアーキテクチャであるためです。動的にロードされます。
TLDR;別のDLLから動的に型をロードする場合は、ビルドイベントを使用してそのDLLの最新バージョンをbinディレクトリにコピーしてください。そうしないと、キャストが機能しないように見える場合があります。 。
同じ問題が発生し、次のコードを追加しました
private void LoadAssemblyPlugins(string dll)
Assembly ass = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => new Uri(a.CodeBase).Equals(new Uri(dll)));
if (ass == null)
// Load it here
// use activator here
本番環境では問題になることはありませんが、単体テストでは問題はありましたが、今は再度ロードして「別のタイプ」を作成する必要はありません。
クラスFPG.H60.AFF.TabControlH6が実際にIFrameworkClientを実装している場合、これが失敗する理由はないはずです。この例外の原因となるのは、IFrameworkClientを含むアセンブリに厳密な名前が付けられていて、Tab Controlオブジェクトが含まれているアセンブリの異なるバージョンを参照している場合、またはIFrameworkClientという名前の異なるインターフェイスを使用している場合だけです。