TypeLoadException говорит "нет реализации", но оно реализовано

У меня очень странная ошибка на нашей тестовой машине. Ошибка:

System.TypeLoadException: Method 'SetShort' in type 'DummyItem' from assembly 'ActiveViewers (...)' does not have an implementation.

Я просто не могу понять почему. SetShort есть ли в DummyItem класс, и я даже перекомпилировал версию с записью в журнал событий, просто чтобы убедиться, что это не проблема развертывания / управления версиями. Странно то, что вызывающий код даже не вызывает SetShort метод.

42 ответа

Решение

ПРИМЕЧАНИЕ. - Если этот ответ вам не поможет, найдите время, чтобы прокрутить список других ответов, добавленных с тех пор.

Короткий ответ

Это может произойти, если вы добавляете метод к интерфейсу в одной сборке, а затем к классу реализации в другой сборке, но вы перестраиваете сборку реализации без ссылки на новую версию сборки интерфейса.

В этом случае DummyItem реализует интерфейс из другой сборки. Метод SetShort был недавно добавлен как к интерфейсу, так и к DummyItem, но сборка, содержащая DummyItem, была перестроена со ссылкой на предыдущую версию сборки интерфейса. Таким образом, метод SetShort эффективен, но без волшебного соуса, связывающего его с эквивалентным методом в интерфейсе.

Длинный ответ

Если вы хотите попробовать воспроизвести это, попробуйте следующее:

  1. Создайте проект библиотеки классов: InterfaceDef, добавьте только один класс и соберите:

    public interface IInterface
    {
        string GetString(string key);
        //short GetShort(string key);
    }
    
  2. Создайте проект библиотеки второго класса: Реализация (с отдельным решением), скопируйте InterfaceDef.dll в каталог проекта и добавьте в качестве ссылки на файл, добавьте только один класс и соберите:

    public class ImplementingClass : IInterface
    {
        #region IInterface Members
        public string GetString(string key)
        {
            return "hello world";
        }
    
        //public short GetShort(string key)
        //{
        //    return 1;
        //}
        #endregion
    }
    
  3. Создайте третий консольный проект: ClientCode, скопируйте две библиотеки в каталог проекта, добавьте ссылки на файлы и добавьте следующий код в метод Main:

     IInterface test = new ImplementingClass();
     string s = test.GetString("dummykey");
     Console.WriteLine(s);
     Console.ReadKey();
    
  4. Запустите код один раз, консоль скажет "Привет, мир"

  5. Раскомментируйте код в двух проектах dll и перестройте - скопируйте два dll обратно в проект ClientCode, перестройте и попробуйте запустить снова. Исключение TypeLoadException возникает при попытке создания экземпляра ImplementingClass.

В дополнение к тому, что уже сказано в ответе самого автора, возможно, стоит отметить следующее. Причина, по которой это происходит, заключается в том, что класс может иметь метод с такой же сигнатурой, что и метод интерфейса, без реализации этого метода. Следующий код иллюстрирует это:

public interface IFoo
{
    void DoFoo();
}

public class Foo : IFoo
{
    public void DoFoo() { Console.WriteLine("This is _not_ the interface method."); }
    void IFoo.DoFoo() { Console.WriteLine("This _is_ the interface method."); }
}

Foo foo = new Foo();
foo.DoFoo();               // This calls the non-interface method
IFoo foo2 = foo;
foo2.DoFoo();              // This calls the interface method

Я получил это, когда в моем приложении не было ссылки на другую сборку, определяющую класс, который использовал метод в сообщении об ошибке. Запуск PEVerify дал более полезную ошибку: "Система не может найти указанный файл".

Я столкнулся с тем же сообщением, и вот что мы нашли: мы используем сторонние библиотеки в нашем проекте. После выхода новой версии мы изменили наш проект, чтобы он указывал на новый набор библиотек, и успешно скомпилировали.

Исключение возникло, когда я попытался установить один из их сопряженных классов во время выполнения. Мы убедились, что все остальные ссылки были актуальны, но все же не повезло. Нам потребовалось некоторое время, чтобы определить (с помощью обозревателя объектов), что возвращаемый тип метода в сообщении об ошибке был совершенно новым типом из новой сборки без ссылки.

Мы добавили ссылку на сборку и ошибка исчезла.

  • Сообщение об ошибке вводило в заблуждение, но указывало более или менее на правильное направление (правильный метод, неправильное сообщение).
  • Исключение произошло, хотя мы не использовали этот метод.
  • Что приводит меня к вопросу: если это исключение выдается в любом случае, почему компилятор не подхватывает его?

Я получил эту ошибку в следующем сценарии.

  • Обе сборки A и B ссылались на System.Web.Mvc версии 3.0.0.0.
  • Assembly A ссылалась на Assembly B и имела классы, которые реализовывали интерфейсы из Assembly B с методами, которые возвращали классы из System.Web.Mvc.
  • Сборка А обновлена ​​до System.Web.Mvc версии 4.0.0.0
  • Сборка C запустила приведенный ниже код (FertPin.Classes.Contact содержался в Сборке A):

var target = Assembly.GetAssembly(typeof(FertPin.Classes.Contact));

Для меня исправлением было обновление ссылки System.Web.Mvc в сборке B до 4.0.0.0. Кажется очевидным сейчас!

Благодаря оригинальному постеру!

В другой раз вы можете получить эту ошибку, если у вас неверная версия подписанной сборки. Это не нормальный симптом для этой причины, но вот сценарий, где я получил его

  • проект asp.net содержит сборку A и сборку B, B строго назван

  • сборка A использует Activator.CreateInstance для загрузки сборки C (т.е. нет ссылки на C, который создается отдельно)

  • C был создан со ссылкой на более старую версию сборки B, чем в настоящее время

надеюсь, что это поможет кому-то - мне понадобились годы, чтобы понять это.

У меня тоже была эта ошибка, она была вызвана тем, что Any CPU exe ссылается на любые сборки CPU, которые в свою очередь ссылаются на сборку x86.

Исключение жаловалось на метод для класса в MyApp.Implementations (Любой ЦП), который получил MyApp.Interfaces (Любой ЦП), но в fuslogvw.exe я обнаружил скрытое исключение "попытка загрузить программу с неверным форматом" из MyApp..CommonTypes (x86), который используется обоими.

Я продолжаю возвращаться к этому... Многие ответы здесь делают большую работу, объясняя, в чем проблема, но не как ее решить.

Решение этой проблемы состоит в том, чтобы вручную удалить файлы bin в каталоге ваших проектов. Это очистит все ссылки и заставит проект использовать новейшие библиотеки DLL.

Я не предлагаю использовать функцию удаления инструментов публикации, потому что это приводит к удалению IIS.

Я столкнулся с этой ошибкой в ​​контексте, где я использовал Autofac и много динамической загрузки сборки.

При выполнении операции разрешения Autofac среда выполнения не сможет загрузить одну из сборок. Сообщение об ошибке жаловалось, что Method 'MyMethod' in type 'MyType' from assembly 'ImplementationAssembly' does not have an implementation, Симптомы возникали при работе на виртуальной машине Windows Server 2012 R2, но не возникали на виртуальных машинах Windows 10 или Windows Server 2016.

ImplementationAssembly ссылка System.Collections.Immutable 1.1.37, и содержал реализации IMyInterface<T1,T2> интерфейс, который был определен в отдельном DefinitionAssembly, DefinitionAssembly ссылка System.Collections.Immutable 1.1.36.

Методы из IMyInterface<T1,T2> которые были "не реализованы", имели параметры типа IImmutableDictionary<TKey, TRow>, который определен в System.Collections.Immutable,

Фактическая копия System.Collections.Immutable в каталоге программы была версия 1.1.37. На моей виртуальной машине Windows Server 2012 R2 GAC содержал копию System.Collections.Immutable 1.1.36. В Windows 10 и Windows Server 2016 GAC содержал копию System.Collections.Immutable 1.1.37. Ошибка загрузки произошла только тогда, когда GAC содержал более старую версию DLL.

Таким образом, основной причиной сбоя загрузки сборки были несоответствующие ссылки на System.Collections.Immutable, Определение и реализация интерфейса имели идентичные сигнатуры методов, но фактически зависели от разных версий System.Collections.ImmutableЭто означало, что среда выполнения не считала класс реализации соответствующим определению интерфейса.

Добавление следующей привязки к моему файлу конфигурации исправило проблему:

<dependentAssembly>
        <assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.1.37.0" newVersion="1.1.37.0" />
</dependentAssembly>

Я получил это с "ромбической" формой зависимости проекта:

  • Проект A использует Проект B и Проект D
  • Проект B использует Проект D

Я перекомпилировал проект A, но не проект B, что позволило проекту B "внедрить" старую версию Project Dll

У меня есть еще одно эзотерическое решение этого сообщения об ошибке. Я обновил свою целевую платформу с.Net 4.0 до 4.6, и мой проект модульного тестирования выдавал мне ошибку "System.TypeLoadException... не имеет реализации" при попытке сборки. Он также дал второе сообщение об ошибке о том же якобы не реализованном методе, который гласил: "Задача"BuildShadowTask"неожиданно завершилась неудачей". Похоже, что ни один из советов здесь не помог, поэтому я искал "BuildShadowTask" и нашел сообщение в MSDN, в котором я использовал текстовый редактор для удаления этих строк из файла csproj проекта модульного тестирования.

<ItemGroup>
  <Shadow Include="Test References\MyProject.accessor" />
</ItemGroup>

После этого обе ошибки исчезли и проект был построен.

Я столкнулся с этим, когда переименовал проект (и имя сборки), от которого зависел проект ASP.NET. Типы в веб-проекте реализованы интерфейсами в зависимой сборке. Несмотря на выполнение Clean Solution из меню Build, сборка с предыдущим именем осталась в bin папка, и когда мой веб-проект выполняется

var types = AppDomain.CurrentDomain.
   GetAssemblies().
   ToList().
   SelectMany( s => s.GetTypes() /* exception thrown in this call */ )
;

Вышеуказанное исключение было выдвинуто с жалобой на то, что методы интерфейса в реализующих веб-типах фактически не были реализованы. Удаление сборки вручную в веб-проекте bin папка решила проблему.

Эта ошибка также может быть вызвана тем, что сборка загружается с использованием Assembly.LoadFrom(String) и ссылается на сборку, которая уже была загружена с использованием Assembly.Load(Byte[]).

Например, вы встраивали ссылочные сборки основного приложения в качестве ресурсов, но ваше приложение загружает подключаемые модули из определенной папки.

Вместо использования LoadFrom вы должны использовать Load. Следующий код сделает работу:

private static Assembly LoadAssemblyFromFile( String filePath )
{
    using( Stream stream = File.OpenRead( filePath ) )
    {
        if( !ReferenceEquals( stream, null ) )
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read( assemblyData, 0, assemblyData.Length );
            return Assembly.Load( assemblyData );
        }
    }
    return null;
}

FWIW, я получил это, когда был файл конфигурации, который перенаправлял на несуществующую версию ссылочной сборки. Логи Fusion для победы!

Я получил эту ошибку, потому что у меня был класс в сборке "C", который был в версии 4.5 платформы, реализовав интерфейс в сборке "A", который был в версии 4.5.1 платформы, и служил в качестве базового класса для сборки 'B', который также был на версии 4.5.1 платформы. Система вызвала исключение при попытке загрузить сборку "B". Кроме того, я установил несколько пакетов nuget для.net 4.5.1 на всех трех сборках. По какой-то причине, даже несмотря на то, что ссылки на nuget не отображались в сборке 'B', он успешно создавался

Оказалось, что реальная проблема заключалась в том, что сборки ссылались на разные версии пакета nuget, который содержал интерфейс, и сигнатура интерфейса менялась между версиями.

Я также получил эту ошибку, когда ранее включил покрытие кода во время модульного тестирования для одной из сборок. По какой-то причине Visual Studio "буферизовала" старую версию этой конкретной библиотеки DLL, хотя я обновил ее, чтобы реализовать новую версию интерфейса. Отключение покрытия кода избавило от ошибки.

Я просто обновил решение с MVC3 до MVC5 и начал получать то же исключение из моего проекта модульного тестирования.

Проверил все ссылки, ища старые файлы, в конечном итоге обнаружил, что мне нужно было выполнить несколько bindingRedirects для Mvc в моем проекте модульного тестирования.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

Другое объяснение проблемы такого типа, связанной с управляемым C++.

Если вы попытаетесь заглушить интерфейс, определенный в сборке, созданной с использованием управляемого C++, которая имеет специальную подпись, вы получите исключение при создании заглушки.

Это верно для Rhino Mocks и, вероятно, для любой фальшивой среды, которая использует System.Reflection.Emit,

public interface class IFoo {
  void F(long bar);
};

public ref class Foo : public IFoo {
public:
  virtual void F(long bar) { ... }
};

Определение интерфейса получает следующую подпись:

void F(System.Int32 modopt(IsLong) bar)

Обратите внимание, что тип C++ long карты для System.Int32 (или просто int в C#). Это немного неизвестно modopt это вызывает проблему, как заявило Айенде Рахиен в списке рассылки Rhino Mocks.

Я столкнулся почти с той же проблемой. Я почесал голову, что является причиной этой ошибки. Я перепроверил, все методы были реализованы.

На Google, я получил эту ссылку среди других. Основываясь на комментарии @Paul McLink, эти два шага решили проблему.

  1. Перезапустите Visual Studio
  2. Очистить, построить (перестроить)

и ошибка ушла.

Перезапустите плагин VS

Спасибо Пол:)

Надеюсь, это поможет кому-то, кто сталкивался с этой ошибкой:)

В моем случае это помогло сбросить набор инструментов WinForms.

Я получил исключение при открытии Form в дизайнере; однако компиляция и запуск кода были возможны, и код вел себя как ожидалось. Исключение произошло в местном UserControl реализация интерфейса из одной из моих библиотек, на которые есть ссылки. Ошибка возникла после обновления этой библиотеки.

это UserControl был указан в наборе инструментов WinForms. Вероятно, Visual Studio хранила ссылку на устаревшую версию библиотеки или где-то кэшировала устаревшую версию.

Вот как я выздоровел из этой ситуации:

  1. Щелкните правой кнопкой мыши на панели инструментов WinForms и выберите Reset Toolbox в контекстном меню. (Это удаляет пользовательские элементы из панели инструментов).
    В моем случае элементы панели инструментов были восстановлены до состояния по умолчанию; однако стрелка указателя отсутствовала в панели инструментов.
  2. Закройте Visual Studio.
    В моем случае Visual Studio прервана с исключением из-за нарушения и прервана.
  3. Перезапустите Visual Studio.
    Сейчас все идет гладко.

В моем случае я ранее ссылался на mylib проект в папке родного брата за пределами репо - давайте назовем это v1.0,

|-- myrepo
|    |-- consoleApp
|    |-- submodules
|         |-- mylib (submoduled v2.0)
|-- mylib (stale v1.0)

Позже я сделал это правильно и использовал его через подмодуль git. v2.0, Один проект consoleApp однако не был обновлен должным образом. Он все еще ссылался на старый v1.0 проект за пределами моего проекта GIT.

Смущает, хотя *.csproj был явно неправ и указывал на v1.0Visual Studio IDE показал путь как v2.0 проект! F12 для проверки интерфейса и класса перешел к v2.0 версия тоже.

Сборка, помещенная компилятором в папку bin, была v1.0 версия, отсюда и головная боль.

Тот факт, что IDE лгал мне, очень усложнил понимание ошибки.

Решение: Удалены ссылки на проекты из ConsoleApp и прочитал их.

Общий совет: Перекомпилируйте все сборки с нуля (где это возможно, конечно, нельзя для пакетов nuget) и проверьте метки даты и времени в bin\debug папка. Любые старые датированные сборки - ваша проблема.

Это случилось со мной, когда в интерфейсе была ссылка на стороннюю dll (MWArray) с параметром «Определенная версия», установленным на «Истина», в то время как реализованный класс имел ссылку на ту же библиотеку, но с «Особой версией», установленной на «Ложь», поэтому класс и интерфейс имели разные версии ссылки на одну и ту же dll.

Установка обоих на «Определенная версия»: «Ложь» или «Истина» (в зависимости от того, что вам нужно) исправила.

У меня возникла эта проблема, когда я попытался реализовать загрузку сборки плагина в dot net 5 с использованием настраиваемого AssemblyLoadContext (без создания AppDomain) и общего типа (интерфейса, который необходимо использовать для вызова методов плагина без отражения). Ничего из этой ветки мне не помогло. Вот что я сделал для решения этой проблемы:

  1. Чтобы разрешить отладку загрузки плагина без проблем - настройте выходной путь проекта в папку bin приложения хоста. Вы будете отлаживать ту же сборку, что и после сборки проекта плагина. Это, наверное, временное изменение (просто для отладки).
  2. Чтобы исправить исключение TypeLoadException, вам необходимо загрузить все сборки, на которые есть ссылка на «сборку контракта», как общие сборки (кроме сборок времени выполнения). Проверить реализацию контекста загрузчика (загрузка sharedAssemblies в конструкторе):
      
public class PluginAssemblyLoadContext : AssemblyLoadContext
{
    private AssemblyDependencyResolver _resolver;
    private IDictionary<string, Assembly> _loadedAssemblies;
    private IDictionary<string, Assembly> _sharedAssemblies;

    private string _path;

    public PluginAssemblyLoadContext(string path, params Type[] sharedTypes)
         : base(isCollectible: false)
    {

        _path = path;

        var fileInfo = new FileInfo(_path);
        if (fileInfo.Exists)
        {
            _resolver = new AssemblyDependencyResolver(_path);

            _sharedAssemblies = sharedTypes.SelectMany(t => t.Assembly.GetReferencedAssemblies().Select(an =>
            {               
                // we don't need runtime assemblies
                if (_resolver.ResolveAssemblyToPath(an) != null)
                {
                    return Assembly.Load(an);
                }
                return null;
            }).Where(a => a != null)
            .Concat(new[] { t.Assembly })
            ).Distinct().ToDictionary(a => a.Location, StringComparer.OrdinalIgnoreCase);

            _loadedAssemblies = new Dictionary<string, Assembly>();

            var assembly = LoadFromAssemblyPath(fileInfo.FullName);

            _loadedAssemblies.Add(fileInfo.FullName, assembly);
        }
        else
        {
            throw new FileNotFoundException($"File does not exist: {_path}");
        }
    }

    public IEnumerable<Type> GetCommandTypes<T>()
    {
        var cmdType = typeof(T);
        return _loadedAssemblies.Values.SelectMany(a => a.GetTypes()).Where(t => cmdType.IsAssignableFrom(t));
    }

    public IEnumerable<T> CreateCommands<T>(Assembly assembly)
    {
        foreach (var cmdType in GetCommandTypes<T>())
        {
            yield return (T)Activator.CreateInstance(cmdType);
        }
    }

    protected override Assembly Load(AssemblyName assemblyName)
    {
        var path = _resolver.ResolveAssemblyToPath(assemblyName);
        if (path != null)
        {
            if (_sharedAssemblies.ContainsKey(path))
            {
                return _sharedAssemblies[path];
            }
            if (_loadedAssemblies.ContainsKey(path))
            {
                return _loadedAssemblies[path];
            }
            return LoadFromAssemblyPath(path);
        }
        return null; // using default ALC
    }
}

Использование:

      
var loader = new PluginAssemblyLoadContext(fullPath, typeof(IPluginCommand);
loader.CreateCommands<IPluginCommand>()...

Это просто означает, что проект внедрения в моих случаях устарел. DLL, содержащая интерфейс, была перестроена, но dll реализации устарела.

Я получил это в службе WCF из-за того, что был выбран тип сборки x86, в результате чего bisn стал жить под bin\x86 вместо bin. Выбор любого ЦП приводил к тому, что перекомпилированные библиотеки DLL перемещались в правильные места (я не буду вдаваться в подробности того, как это произошло в первую очередь).

Я видел это в Visual Studio Pro 2008, когда два проекта создавали сборки с одинаковыми именами, один из которых был файлом lib SDF.dll, а другой ссылался на библиотеку с именем сборки sdf.exe. Когда я изменил название ссылочной сборки, исключение исчезло

Вот мое мнение об этой ошибке.

Добавил extern метод, но моя паста была неисправна. DllImportAttribute получил закомментированную строку.

/// <returns>(removed for brevity lol)</returns>[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWindowVisible(IntPtr hWnd);

Обеспечение фактического включения атрибута в исходный код решило проблему.

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

Просто чтобы добавить мой опыт, поскольку он не был рассмотрен в других ответах:

Я получил эту проблему при запуске модульных тестов в MsTest.

Тестируемые классы находились в подписанной сборке.

Другая версия этой сборки оказалась в GAC (но с тем же номером версии сборки).

Алгоритм разрешения зависимостей для сборок со строгими именами немного отличается от неподписанных, поскольку сначала проверяется GAC.

Таким образом, MsTest собирал сборку GAC вместо того, чтобы использовать сборку из папки bin, и пытался запустить тесты для нее, которая, очевидно, не работала.

Решением было удалить сборку GAC.

Обратите внимание, что подсказка для меня заключалась в том, что этого не происходило на сервере сборки во время выполнения тестов, поскольку сборка компилировала сборки с новым номером версии сборки. Это означало, что более старые версии сборки в GAC не будут подняты.

Кроме того, я нашел здесь возможный обходной путь, если по какой-то причине вы не можете получить доступ к GAC: https://johanleino.wordpress.com/2014/02/20/workaround-for-unit-testing-code-that-reside-in-the-gac/

Как дополнение: это также может произойти, если вы обновите пакет nuget, который использовался для создания поддельной сборки. Скажем, вы устанавливаете V1.0 пакета nuget и создаете сборку подделок "fakeLibrary.1.0.0.0.Fakes". Затем вы обновляете до последней версии пакета nuget, скажем, v1.1, который добавил новый интерфейс к интерфейсу. Библиотека Fakes все еще ищет v1.0 библиотеки. Просто удалите поддельную сборку и восстановите ее. Если это была проблема, это, вероятно, исправит это.

Другие вопросы по тегам