TypeLoadException says 'no implementation', but it is implemented

Viewed 126741

I've got a very weird bug on our test machine. The error is:

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

I just can't understand why. SetShort is there in the DummyItem class, and I've even recompiled a version with writes to the event log just to make sure that it's not a deployment/versioning issue. The weird thing is that the calling code doesn't even call the SetShort method.

41 Answers

NOTE - If this answer doesn't help you, please take the time to scroll down through the other answers that people have added since.

Short answer

This can happen if you add a method to an interface in one assembly, and then to an implementing class in another assembly, but you rebuild the implementing assembly without referencing the new version of the interface assembly.

In this case, DummyItem implements an interface from another assembly. The SetShort method was recently added to both the interface and the DummyItem - but the assembly containing DummyItem was rebuilt referencing the previous version of the interface assembly. So the SetShort method is effectively there, but without the magic sauce linking it to the equivalent method in the interface.

Long answer

If you want to try reproducing this, try the following:

  1. Create a class library project: InterfaceDef, add just one class, and build:

    public interface IInterface
    {
        string GetString(string key);
        //short GetShort(string key);
    }
    
  2. Create a second class library project: Implementation (with separate solution), copy InterfaceDef.dll into project directory and add as file reference, add just one class, and build:

    public class ImplementingClass : IInterface
    {
        #region IInterface Members
        public string GetString(string key)
        {
            return "hello world";
        }
    
        //public short GetShort(string key)
        //{
        //    return 1;
        //}
        #endregion
    }
    
  3. Create a third, console project: ClientCode, copy the two dlls into the project directory, add file references, and add the following code into the Main method:

     IInterface test = new ImplementingClass();
     string s = test.GetString("dummykey");
     Console.WriteLine(s);
     Console.ReadKey();
    
  4. Run the code once, the console says "hello world"

  5. Uncomment the code in the two dll projects and rebuild - copy the two dlls back into the ClientCode project, rebuild and try running again. TypeLoadException occurs when trying to instantiate the ImplementingClass.

In addition to what the asker's own answer already stated, it may be worth noting the following. The reason this happens is because it is possible for a class to have a method with the same signature as an interface method without implementing that method. The following code illustrates that:

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

FWIW, I got this when there was a config file that redirected to a non-existent version of a referenced assembly. Fusion logs for the win!

I got this in a WCF service due to having an x86 build type selected, causing the bins to live under bin\x86 instead of bin. Selecting Any CPU caused the recompiled DLLs to go to the correct locations (I wont go into detail as to how this happened in the first place).

I got this issue when I tried to implement plugin assembly loading in dot net 5 using custom AssemblyLoadContext (without AppDomain creation) and shared type (interface which you need to use to call plugin methods without reflection). Nothing from this thread helped me. Here is what I did to solve this issue:

  1. To allow debugging of plugin loading without a problems - setup project output path to host app bin folder. You will debug the same assembly you got after plugin project build. This is, probably, temporary change (just for debug).
  2. To fix TypeLoadException you need to load all 'contract assembly' referenced assemblies as shared assemblies (except runtime assemblies). Check loader context implementation (loading sharedAssemblies in constructor):

    public class PluginAssemblyLoadContext : AssemblyLoadContext
    {
        private AssemblyDependencyResolver _resolver;
        
        private IDictionary<string, Assembly> _loadedAssemblies;
        
        private IDictionary<string, Assembly> _sharedAssemblies;

        private AssemblyLoadContext DefaultAlc;

        private string _path;

        public PluginAssemblyLoadContext(string path, bool isCollectible, params Type[] sharedTypes)
             : this(path, isCollectible, sharedTypes.Select(t => t.Assembly).ToArray())
        {
        }

        public PluginAssemblyLoadContext(string path, bool isCollectible, params Assembly[] sharedAssemblies)
             : base(isCollectible: isCollectible)
        {

            _path = path;

            DefaultAlc = GetLoadContext(Assembly.GetExecutingAssembly()) ?? Default;

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

                _sharedAssemblies = new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);
                foreach (var a in sharedAssemblies.Distinct())
                {
                    LoadReferencedAssemblies(a);
                }

                _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 bool LoadReferencedAssemblies(Assembly assembly)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException(nameof(assembly));
            }
            if (string.IsNullOrEmpty(assembly.Location))
            {
                throw new NotSupportedException($"Assembly location is empty string or null: {assembly.FullName}");
            }
            var alc = GetLoadContext(assembly);
            if (alc == this)
            {
                throw new InvalidOperationException($"Circular assembly loader dependency detected");
            }
            if (!_sharedAssemblies.ContainsKey(assembly.Location))
            {
                _sharedAssemblies.Add(assembly.Location, assembly);

                foreach (var an in assembly.GetReferencedAssemblies())
                {
                    var ra = alc.LoadFromAssemblyName(an);
                    LoadReferencedAssemblies(ra);
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        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 DefaultAlc.LoadFromAssemblyName(assemblyName);
        }
    }

Usage:


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

I get this error today. My problem was - do not doing in TFS get latest version. In server was dll with interface whic one of methods was modified. I worked with an old one - in my PC its works. How to fix: get latest, rebuild

Yet another way to get this:

class GenericFoo<T> {}

class ConcreteFoo : GenericFoo<ClassFromAnotherAssembly> {}

Code in an assembly that doesn't reference the assembly of ClassFromAnotherAssembly.

var foo = new ConcreteFoo(); //kaboom

This happened to me when ClassFromAnotherAssembly was ValueTuple.

It happened to me when an interface had a reference to 3rd party dll (MWArray) with 'Specific Version' set to 'True' while the implemented class had a reference to the same dll but with 'Specific Version' set to 'False', so class and interface had different versions reference to the same dll.

Setting both to 'Specific Version': 'False' or 'True' (depending on what you need) fixed it.

Just to add my experience as it has not been covered in other answers:

I got this problem when running unit tests in MsTest.

The classes under test were in a signed assembly.

A different version of this assembly happened to be in the GAC (but with the same assembly version number).

Dependency resolution algorithm for strong named assemblies is slightly different to non-signed as the GAC is checked first.

So MsTest was picking up the GAC'd assembly rather than using the one from the bin folder, and trying to run the tests against it, which was obviously not working.

Solution was to remove the GAC'd assembly.

Note, the clue for me was this was not happening on the build server when the tests were run, because the build would compile the assemblies with a new assembly version number. This meant that older versions of the assembly in the GAC would not be picked up.

Also, I found a potential workaround here, if you cannot for some reason access the GAC: https://johanleino.wordpress.com/2014/02/20/workaround-for-unit-testing-code-that-reside-in-the-gac/

I ran into this and only my local machine was having the problem. No other developers in our group nor my VM had the problem.

In the end it seemed to be related to a "targeting pack" Visual Studio 2017

  • Open the Visual Studio Installer
  • Select Modify
  • Go to the second top tab "Individual Components"
  • See what Frameworks and Targeting packs you have selected.
  • I did not have the two newest targeting packs selected
  • I also noticed I did not have "Advanced ASP.NET features" selected and the other machines did.
  • Selected and installed the new items and it is all good now.

Our problem solved with updating windows! Our web application is on .Net 4.7.1 and c# 7.0. As we tested in different windowses, we understood that the problem will be solved by updating windows. Indeed, the problem was seen in windows 10 (version 1703) and also in a windows server 2012(not updated in last year). After updating both of them, the problem was solved. In fact, the asp.net minor version(the third part of the clr.dll version in C:\Windows\Microsoft.NET\Framework64\v4.0.30319 ) was changed a bit after the update.

I had this error when my integration test project was trying to load a DLL which didn't contain dependency resolution for an interface:

  1. Integration Test Project (references Main Project but not StructureMap)
  2. Main Project (references StructureMap project - uses Interface in class constructor)
  3. StructureMap Project (IoC - For().Use();)

This caused the error to be thrown because it couldn't find the concrete implementation. I excluded the DLL in my test configuration and the error disappeared

The solution for me was related to the fact that the project that was implementing the interface had the property "Register for COM Interop" set. Unchecking this option resolved the issue for me.

What solved the problem for me was to add the following to ProjectReference and/or PackageReference along with <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> in the Assembly which was being loaded:

<Private>false</Private>
<ExcludeAssets>runtime</ExcludeAssets>

This then made my project file look something like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
  </PropertyGroup>

  <!-- For a package -->
  <ItemGroup>
    <PackageReference Include="SomePackage" Version="x.x.x.x">
      <Private>false</Private>
      <ExcludeAssets>runtime</ExcludeAssets>
    </PackageReference>
  </ItemGroup>

  <!-- For a project -->
  <ItemGroup>
    <ProjectReference Include="..\SomeProject\SomeProject.csproj">
      <Private>false</Private>
      <ExcludeAssets>runtime</ExcludeAssets>
    </ProjectReference>
  </ItemGroup>
</Project>
Related