.NET: Невозможно использовать объект для взаимодействия с ним.
У меня есть класс (TabControlH60), который наследуется от базового класса (UserControl) и реализует интерфейс (IFrameworkClient). Я создаю экземпляр объекта с помощью класса .NET Activator. С возвращенным экземпляром я могу применить к базовому классу UserControl, но не к интерфейсу. Исключение, которое я получаю, находится ниже кода snipet. Как мне перейти на интерфейс?
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
У меня те же проблемы с моей библиотекой, обеспечивающей "плагиновую" функциональность... Я наконец-то ее обработал...
Вот моя проблема: у меня была одна основная сборка с использованием плагинов, одна сборка с плагином (Plugin.dll) И (важная) другая сборка, обеспечивающая плагин-функциональность (Library.dll).
Плагин .dll ссылается на основную сборку (для ее расширения) и Library.dll с помощью функции plugin-func. - его двоичные файлы попали в каталог "./Plugins" относительно основной сборки.
В основной сборке также упоминается функция plugin-func. сборка для использования "PluginManager" написана. Этот "PluginManager" получает путь и загружает все файлы *.dll через отражение, чтобы проанализировать, есть ли "IPlugin" -интерфейс (который также поступает из Library.dll).
Каждый раз, когда я вызывал PluginManager для загрузки плагинов, он не мог отнести их к "IPlugin" , хотя они реализовали его.
Я чуть не рассердился, но потом выяснил всю проблему. При компиляции плагина в директорию "./Plugins" была записана не только "Plugin.dll", но и "Library.dll" . Случайно загружая "Library.dll" каждый раз с помощью моего модуля PluginManager, я теперь имел два типа "IPlugin" - один в фактической "Library.dll" , который используется из основной сборки и тот, который был загружен через мой PluginManager - и они были несовместимы!
Внимание - если вы просто не загружаете "./Plugins/Library.dll", вы все же сталкиваетесь с проблемой - потому что если вы загружаете "Plugin.dll", который ссылается на "Library.dll" , то он просто использует тот, который находится в тот же каталог... TILT...!! Мой PluginManager теперь просто удаляет "Library.dll" , где он его находит.
Подсказка: убедитесь, что вы не имеете доступа к двум сборкам в разных контекстах!
Ответ 2
Наиболее вероятная причина здесь в том, что IFrameworkClient
является из другой сборки в двух случаях и, следовательно, является другим типом .NET. Даже если это тот же код, он может быть другого типа.
Проверьте AssemblyQualifiedName
. Также обратите внимание, что если вы загружаете эту сборку с отражением, вы можете получить другой тип даже с тем же AssemblyQualifiedName, благодаря контексту load.
Ответ 3
Определите интерфейс IFrameworkClient в независимом пространстве имен (должно быть в пространстве имен) независимого проекта (библиотеки классов). Затем добавьте refrence библиотеки классов для управления проектом и основным проектом
Ответ 4
Что-то подсказывает, что ваш образец кода оставляет некоторые вещи...
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 { }
Это компилируется и выполняется.
Я уверен, что DLL, содержащая определение IFrameworkClient, еще не загружена до того, как вы попытаетесь выполнить бросок. Это может произойти, если вы используете Activator.CreateInstance.
Попробуйте вставить var forceLoad = typeof(IFrameworkClient);
перед трансляцией.
Ответ 5
Когда Interface
в другой сборке и я получаю мой класс динамически в run-time
в другой сборке, interface casting
будет сбой, как ваш sample (С# знает наш интерфейс как другой тип, чем тот, который унаследовал от этого класса).
Это мой простой и полезный метод в следующих случаях:
Когда я уверен, что мой Class
унаследовал от упомянутого Interface
(eq. IFrameworkClient
), поэтому я пишу одну магическую строку кода следующим образом:
dynamic fc = obj as IFrameworkClient ?? (dynamic) obj;
С помощью этой техники вы можете:
- Введите коды после этой строки кода для
fc
в design time
на основе Interface members
информации и системы управления версиями.
- Предотвратить любую ошибку литья интерфейса при
run-time
Примечания:
- Вам нужно
C# v4
использовать dynamic
type
- Обычно я не люблю использовать типы
dynamic
в моих кодах, но в некоторых случаях это может нам помочь.
Ответ 6
Используйте оператор as:)
...
m_Client = obj as IFrameworkClient;
if (m_Client != null) //Remember to check for null in case the cast fails
...
...
Ответ 7
Если класс FPG.H60.AFF.TabControlH60 действительно реализует IFrameworkClient, не должно быть причин, по которым это не получится. Единственное, что я могу придумать, вызывает это исключение, если сборка, содержащая IFrameworkClient, сильно пронумерована, и объект Tab Control ссылается на другую версию содержащей сборки, или вы используете другой интерфейс с именем IFrameworkClient.
Ответ 8
В моем случае мне пришлось добавить событие сборки, чтобы скопировать нужную DLL с момента создания экземпляров и назначения типов интерфейсов во время выполнения. В противном случае загруженная DLL может не быть самой современной DLL и поэтому не может быть передана в интерфейс.
Причина, по которой я использовал события сборки в этом случае (вместо добавления DLL в качестве ссылки), заключается в том, что архитектура такова, что основное приложение должно ссылаться только на типы интерфейса, а все остальное должно загружаться динамически.
TL;DR;
В случае загрузки типов динамически из другой DLL, убедитесь, что вы скопируете самую последнюю версию этой DLL в каталог bin, используя события сборки, иначе кастинг может не работать, когда кажется, что он должен.
Ответ 9
Я столкнулся с той же проблемой, и я просто добавил следующий код
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
Хотя в производстве это никогда не будет проблемой, в модульном тесте это было, но теперь мне не нужно загружать его снова и создавать "другой тип"
Ответ 10
Приведение не работает, потому что вы пытаетесь использовать тип object
для интерфейса. Если вы замените линию литья интерфейса:
IFrameworkClient fc = (IFrameworkClient)m_Client;
Он будет работать.
В качестве альтернативы, я мягко уверен, что вы можете сделать трансляцию из объекта в интерфейс с помощью оператора as
.
См. эту статью для получения дополнительной информации:
http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx
Еще один кусочек головоломки. Интерфейсы не связаны с object
:
http://blogs.msdn.com/ericlippert/archive/2009/08/06/not-everything-derives-from-object.aspx