[Обновлено 25 мая 2010 года]
Я недавно обновил VS2008 до VS2010 и в то же время обновился до .Net 4.
Я перекомпилировал существующее решение и столкнулся с исключением Cast, которого раньше не было.
Структура кода проста (хотя фактическая реализация несколько сложнее).
В основном у меня есть:
public class SomeClass : ISomeClass
{
// Stuff
}
public static class ClassFactory
{
public static IInterface GetClassInstance<IInterface>(Type classType)
{
return (IInterface)Activator.CreateInstance(classType); // This throws a cast exception
}
}
// Call the factory with:
ISomeClass anInstance = ClassFactory.GetClassInstance<ISomeClass>(typeof(SomeClass));
Игнорируйте «разумность» вышесказанного - он предоставляет только представление о проблеме, а не специфику того, что я делаю (например, параметры конструктора были удалены).
Отмеченная строка вызывает исключение:
Невозможно привести объект типа
Namespace.SomeClass для ввода
'Namespace.ISomeClass'.
Я подозреваю, что это может иметь какое-то отношение к дополнительной безопасности DotNet (и, в частности, к явной загрузке сборок, поскольку это то, что делает мое приложение).
Причина, по которой я подозреваю, заключается в том, что мне пришлось добавить в конфигурационный файл параметр:
<runtime>
<loadFromRemoteSources enabled="true" />
</runtime>
.. но я не уверен, связано ли это.
Обновление
Я вижу (из комментариев), что мой основной код сам по себе не воспроизводит проблему. Не удивительно, я полагаю. Будет сложно определить, какая часть большой 3-уровневой системы CQS имеет отношение к этой проблеме.
Одной из проблем может быть то, что задействовано несколько сборок. Мой статический класс на самом деле является поставщиком фабрики, а SomeClass - фабрикой классов (важно, чтобы фабрики «регистрировались» в приложении через явную загрузку сборки / типа - см. Ниже).
Upfront. Я использую рефлексию, чтобы «зарегистрировать» все фабрики (то есть классы, реализующие определенный интерфейс), и делаю это, когда приложение запускается путем определения соответствующих сборок, загрузки их и добавления их в кеш, используя (по сути) :
Loop over (file in files)
{
Assembly assembly = Assembly.LoadFile(file);
baseAssemblyList.Add(assembly);
}
Затем я кеширую доступные типы в этих сборках:
foreach (Assembly assembly in _loadedAssemblyList)
{
Type[] assemblyTypes = assembly.GetTypes();
_loadedTypesCache.AddRange(assemblyTypes);
}
А затем я использую этот кеш для выполнения различных операций отражения, включая «регистрацию» фабрик, которая включает в себя циклическое прохождение всех загруженных (кэшированных) типов и поиск тех, которые реализуют (базовый) интерфейс Factory.
В прошлом я сталкивался с подобной проблемой (.Net 3.5, поэтому не совсем такой) с архитектурой, которая включала динамическое создание классов на сервере и потоковую передачу скомпилированного двоичного файла этих классов в клиентское приложение. , Проблема возникла при попытке десериализации экземпляра динамического класса на клиенте с помощью удаленного вызова: исключение говорит о том, что тип класса не известен, хотя типы источника и назначения были в точности одинаковыми (включая пространство имен). По существу, межграничные версии класса не были признаны одинаковыми. Я решил это путем перехвата процесса десериализации и явного определения типа класса десериализации в контексте локальных сборок.
Этот опыт заставляет меня думать, что типы считаются несовпадающими, потому что (каким-то образом) интерфейс фактического объекта SomeClass и интерфейс, переданный в метод Generic, не считаются одним и тем же типом.
Итак (возможно) мой вопрос для тех, кто более осведомлен о C # / DotNet: как работает загрузка классов, так как мое приложение думает, что есть два варианта / типа интерфейса, и как я могу это исправить (помня это проблема DotNet 3.5 vs 4, как она работала до моего обновления)?
[фу ... кто здесь попал, тот терпеливый .. спасибо]