动态类型的神秘感双重调度

时间:2014-11-19 08:07:22

标签: c# c#-4.0 dynamic structuremap structuremap3

最近我遇到了一个尝试通过动态类型实现双重调度的有趣问题。

一点背景:在我的一个项目中,我使用StructureMap容器​​和动态类型作为在运行时调度方法调用的干净方式。在将StructureMap容器​​更新到较新版本(3)之后,我的一些单元测试开始永久挂起。

为了重现这个问题,我创建了2个最大限度简化的单元测试:第一个测试永远挂在标有(*)的行上,第二个测试按预期通过。它们之间的唯一区别是第一个方法返回StructureMap的LambdaInstance类型的对象。

悬挂测试:

    [TestFixture]
    [Category("Unit")]
    public class when_trying_to_call_method_with_dynamic_argument1
    {            
        private class A {}

        private static LambdaInstance<object> Method(A obj)
        {
            throw new NotImplementedException();
        }

        [Test]
        [ExpectedException(typeof(NotImplementedException))]
        public void should_succeed()
        {
            var instance = (dynamic)new A();
            Method(instance); //(*)hangs forever on this line              
        }
    }

通过考试:

    [TestFixture]
    [Category("Unit")]
    public class when_trying_to_call_method_with_dynamic_argument2
    {            
        private class A {}

        private static object Method(A obj)
        {
            throw new NotImplementedException();
        }

        [Test]
        [ExpectedException(typeof(NotImplementedException))]
        public void should_succeed()
        {
            var instance = (dynamic)new A();
            Method(instance);
        }
    }

怎么可能?或者我只是累了,需要睡觉?

无论如何,它是概念和教育问题,而不是愿意为特定图书馆中的特定问题找到解决方法。

UPDATE1: 已验证4.0&amp; 4.5目标框架,在VS2010(SP1),VS2013中验证。

UPDATE2: 简单的控制台应用程序也挂在同一行(因此,它不是测试运行器的问题):

class Program
{
    private class A { }

    private static LambdaInstance<object> Method(A obj)
    {
        throw new NotImplementedException();
    }

    static void Main(string[] args)
    {
        var instance = (dynamic)new A();
        Method(instance); //(*)hangs forever on this line            
    }
}

我也在GitHub创建了独立示例。

1 个答案:

答案 0 :(得分:1)

该问题位于StructureMap的LambdaInstance<T>类继承中。 C#dynamic的使用涉及创建多态callite,其中确实使用运行时绑定器。

考虑LambdaInstance类的简化继承树:

class Program
{
    private static LambdaInstance<object> Method(object obj)
    {
        throw new NotImplementedException();
    }

    static void Main(string[] args)
    {
        var instance = (dynamic)new object();
        Method(instance);
    }
}

public class LambdaInstance<T> : LambdaInstance<T, T>
{

}

public class LambdaInstance<T, TPluginType> : ExpressedInstance<LambdaInstance<T, TPluginType>, T, TPluginType>
{

}

public abstract class ExpressedInstance<T>
{

}

public abstract class ExpressedInstance<T, TReturned, TPluginType> : ExpressedInstance<T>
{

}

如上所示,LambdaInstance<T, TPluginType>继承ExpressedInstance<T, TReturned, TPluginType>,但具有T:LambdaInstance<T, TPluginType>的专业化。 因此,通用参数T的特化是子类型定义 - LambdaInstance<T, TPluginType>。 如果在运行时根据运行时绑定程序的要求获取构造类型,则会创建循环引用,以获取不变的多态行为。

如果您需要问题来源,请查看Microsoft.CSharp.RuntimeBinder.SymbolTable的私有方法 LoadSymbolsFromType(Type originalType) GetConstructedType(Type type,AggregateSymbol agg) class(Microsoft.CSharp.dll程序集)。 LoadSymbolsFromType和GetConstructedType方法相互调用,而实例化新类型

要在没有框架源的情况下进行检查,请尝试通过提供预定义类型(例如System.Int32)来剖析泛型专业化。

class Program
{
    private static LambdaInstance<object> Method(object obj)
    {
        throw new NotImplementedException();
    }

    static void Main(string[] args)
    {
        var instance = (dynamic)new object();
        Method(instance);
    }
}

public class LambdaInstance<T> : LambdaInstance<T, int>
{

}

public class LambdaInstance<T, TPluginType> : ExpressedInstance<LambdaInstance<T, TPluginType>, int, TPluginType>
{

}

public abstract class ExpressedInstance<T>
{

}

public abstract class ExpressedInstance<T, TReturned, TPluginType> : ExpressedInstance<T>
{

}

运行应用程序。 System.StackOverflowException将被抛出。 使用调试器和反汇编模式 - 问题源是 System.RuntimeTypeHandle.Instantiate(System.Type [])