我们的测试机器上有个很奇怪的bug。错误是:
系统。来自程序集“activeviewer(…)”的类型“DummyItem”中的方法“SetShort”没有实现。
我就是不明白为什么。SetShort在DummyItem类中,我甚至重新编译了一个版本,写入事件日志,只是为了确保它不是部署/版本控制问题。奇怪的是,调用代码甚至不调用SetShort方法。
我们的测试机器上有个很奇怪的bug。错误是:
系统。来自程序集“activeviewer(…)”的类型“DummyItem”中的方法“SetShort”没有实现。
我就是不明白为什么。SetShort在DummyItem类中,我甚至重新编译了一个版本,写入事件日志,只是为了确保它不是部署/版本控制问题。奇怪的是,调用代码甚至不调用SetShort方法。
当前回答
这类问题的另一种解释涉及托管c++。
如果您试图存根使用托管c++创建的具有特殊签名的程序集中定义的接口,您将在创建存根时得到异常。
对于Rhino mock和任何使用System.Reflection.Emit的mock框架都是如此。
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(或者在c#中简称为int)。正如Ayende Rahien在Rhino Mocks邮件列表中所述,这是一个有点晦涩的modopt导致的问题。
其他回答
在最近的一次windows更新后,我收到了这个错误。我设置了一个从接口继承的服务类。该接口包含一个返回ValueTuple的签名,这是c#中的一个相当新的特性。
我所能猜到的是,windows更新安装了一个新的,但即使显式引用它,更新绑定重定向,等等…最终的结果只是将方法的签名更改为“标准”的东西。
注:如果这个答案对你没有帮助,请花点时间向下滚动人们添加的其他答案。
简短的回答
如果将方法添加到一个程序集中的接口,然后再添加到另一个程序集中的实现类,但在没有引用接口程序集中的新版本的情况下重新构建实现程序集中,就会发生这种情况。
在本例中,DummyItem实现了来自另一个程序集的接口。SetShort方法最近被添加到接口和DummyItem中—但是包含DummyItem的程序集是引用先前版本的接口程序集重新构建的。因此SetShort方法有效地存在,但没有将其链接到接口中的等效方法。
长回答
如果你想尝试复制这个,试试下面的方法:
Create a class library project: InterfaceDef, add just one class, and build: public interface IInterface { string GetString(string key); //short GetShort(string key); } 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 } 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(); Run the code once, the console says "hello world" 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.
如果使用assembly . loadfrom (String)加载程序集,并且正在引用已经使用assembly . load (Byte[])加载的程序集,也可能导致此错误。
例如,你嵌入了主应用程序的引用程序集作为资源,但你的应用程序从特定的文件夹加载插件。
你应该使用Load而不是LoadFrom。下面的代码将完成这项工作:
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;
}
当我尝试在dot net 5中使用自定义的AssemblyLoadContext(没有AppDomain创建)和共享类型(你需要使用它来调用插件方法而没有反射)实现插件程序集加载时,我得到了这个问题。这篇文章对我没有任何帮助。以下是我解决这个问题的方法:
为了允许调试插件加载没有问题-设置项目输出路径到主机应用程序bin文件夹。您将调试插件项目构建后得到的相同程序集。这可能是临时更改(仅用于调试)。 要修复TypeLoadException异常,你需要将所有“契约程序集”引用的程序集加载为共享程序集(运行时程序集除外)。检查加载器上下文实现(在构造函数中加载sharedAssemblies):
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);
}
}
用法:
var loader = new PluginAssemblyLoadContext(fullPath, false, typeof(IPluginCommand));
loader.CreateCommands<IPluginCommand>()...
除了提问者自己已经说过的答案之外,还有以下几点值得注意。发生这种情况的原因是,类可能拥有与接口方法具有相同签名的方法,而无需实现该方法。下面的代码说明:
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