【问题标题】:Invoking DynamicMethod on mscorlib generic type with .Net Standard generic parameter causes MissingMethodException使用 .Net Standard 泛型参数对 mscorlib 泛型类型调用 DynamicMethod 会导致 MissingMethodException
【发布时间】:2018-08-02 10:24:38
【问题描述】:

那么这里发生了什么?

这是背景故事。我正在尝试将 .net 标准库集成到 .net 框架产品中。此 .net 框架产品使用旧的 Microsoft.Practices 框架,该框架使用 ObjectBuilder 作为服务容器。

现代 IoC 容器可以做的一件事是 ObjectBuilder 无法做到的,那就是处理同一服务类型的多个实现,并在请求时注入该服务类型的 IEnumerable。为了处理这个问题,我要手动添加一个 List 并在添加到对象构建器时手动添加每个实现。

ObjectBuilder 在 Microsoft.Practices 的 WebClientSoftwareFactory 中使用时,使用 IL 为容器构造对象并将依赖项注入属性。所以即使你添加了一个单例实例,它仍然会尝试使用 IL 注入到实例的属性中。

尝试将 .Net 标准类型的 List 添加到 ObjectBuilder 时发生错误。我已经能够在控制台应用程序中复制导致错误的原因。

这是在 .net framework 4.7.2 上运行的控制台应用程序

namespace ILTest
{
    class Program
    {
        delegate string Echo(string value);

        static void Main(string[] args)
        {
            Console.WindowWidth = Console.WindowWidth * 2;

            EchoOn("1324");
            EchoOn(new List<string>());
            EchoOn(new NetStandardType());
            EchoOn(new List<NetStandardType>()); // this line fails
            EchoOn(new GenericNetStandardType<string>());
            EchoOn(new GenericNetStandardType<NetStandardType>());

            Console.ReadLine();
        }

        static void EchoOn<T>(T obj)
        {
            var type = typeof(T);
            var value = Guid.NewGuid().ToString();
            var echo = CreateEchoMethodOn(obj);
            try
            {
                var echoed = echo(value);
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine($"Successful il on {type.FullName}");
            }
            catch(Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"Failed il on {type.FullName}");
                Console.WriteLine(ex);
            }
        }

        static Echo CreateEchoMethodOn(object obj)
        {
            var type = obj.GetType();
            var method = new DynamicMethod($"Echo_{type.FullName}", typeof(string), new[] { typeof(string) }, type);
            var il = method.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ret);
            return (Echo)method.CreateDelegate(typeof(Echo));
        }
    }
}

这些是 .net 标准 2.0 库中的类

namespace StandardLib
{
    public class NetStandardType
    {
    }

    public class GenericNetStandardType<T>
    {
    }
}

除了 List 上的一个之外,所有的 DynamicMethods 都是成功的。那个失败并出现 MissingMethodException。

  • 字符串
    • .net 框架类型
    • DynamicMethod 成功
  • 列表
    • .net 框架泛型类型
    • .net 框架通用参数
    • DynamicMethod 成功
  • 网络标准类型
    • .net 标准类型
    • DynamicMethod 成功
  • 列表
    • .net 框架泛型类型
    • .net 标准泛型参数
    • DynamicMethod 失败
  • GenericNetStandardType
    • .net 标准泛型类型
    • .net 框架通用参数
    • DynamicMethod 成功
  • GenericNetStandardType
    • .net 标准泛型类型
    • .net 标准泛型参数
    • DynamicMethod 成功

那么,再次,这里发生了什么?是不是DynamicMethod in.net framework的bug?

【问题讨论】:

  • 我可以用你分享的代码重现这个。但是,我也能够使用来自与应用程序相同的程序集的类型来重现它。这看起来不像是 .NET Standard 的特定问题。我能够通过将 List 替换为 Hashset 来完成这项工作,因此 List 有一些特别之处。我将深入挖掘,看看我是否能弄清楚 List 有什么特别之处会阻止它工作。

标签: c# .net generics dynamic .net-standard-2.0


【解决方案1】:

我相信您看到这种行为是因为代码访问安全性和透明度规则的工作方式复杂。

当您使用 List 作为 DynamicMethod 的类型所有者时,您是在指示运行时使用该类型的 CAS 选项。当您使用用户定义的类型(在 .NET 4 上)时,您的类型成员实际上被标记为“关键”,而“列表”和“列表”类型上的成员被标记为“透明”。

因为您尝试从“透明”类型调用“关键”代码,所以您会收到您提到的错误。

我相信您可以通过使用此属性标记具有您的用户类型的程序集来解决此问题:

[assembly: System.Security.SecurityTransparent]

虽然这应该可行,但很难说这是否会产生任何意想不到的后果。我相信这取决于你的情况。请查看this document 了解更多信息。

【讨论】:

  • 问题是类型不一定是我的。我确实通过创建一个新的 .net 标准类来“解决”这个问题。公共类 SafeList : List {}
猜你喜欢
  • 1970-01-01
  • 2010-10-01
  • 1970-01-01
  • 2013-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多