铸造动态对象并传递到 UnitOfWork 和存储库模式。抛出异常

Casting Dynamic Object and Passing Into UnitOfWork and Repository Pattern. Throws Exception

这是一个非常具体的问题。不太确定怎么说。基本上我正在实现工作单元和存储库模式,我有一个转换为 int 的动态对象,但是如果我使用 var 它会在尝试调用该方法时抛出异常。

我试图尽可能地删除这个问题的所有微不足道的变量。出于某种原因,我只看到这两种设计模式会发生这种情况。我得到的例外是 Additional information: 'BlackMagic.ITacoRepo' does not contain a definition for 'DoStuff'

代码如下:

class BlackMagic
{
    static void Main(string[] args)
    {
        dynamic obj = new ExpandoObject();
        obj.I = 69;

        UnitOfWork uow = new UnitOfWork();

        int i1 = Convert.ToInt32(obj.I);
        var i2 = Convert.ToInt32(obj.I);

        if(i1.Equals(i2))
        {
            uow.TacoRepo.DoStuff(i1); // Works fine
            uow.TacoRepo.DoStuff(i2); // Throws Exception
        }
    }
}

class UnitOfWork
{
    public ITacoRepo TacoRepo { get; set; }

    public UnitOfWork()
    {
        TacoRepo = new TacoRepo();
    }
}

class Repo<T> : IRepo<T> where T : class
{
    public void DoStuff(int i)
    {
    }
}

interface IRepo<T> where T : class
{
    void DoStuff(int i);
}

class TacoRepo : Repo<Taco>, ITacoRepo
{
}

interface ITacoRepo : IRepo<Taco>
{
}

class Taco
{
}

编辑: 我试图找到答案的主要问题是,为什么在工作单元内调用 DoStuff 会抛出异常(而使用回购)但如果 DoStuff 存在于 BlackMagic class.

中则不会被抛出

看起来 RuntimeBinder 没有遍历继承层次结构,所以它只在直接接口ITacoRepo中查找 DoStuff 的定义。

如果让 UnitOfWork 使用 IRepo<Taco> 而不是 ITacoRepo,它能够找到方法定义。

这是我 5 多年前向 Microsoft 报告的错误之一,在 dynamic 引入后不久。据我所知,它在他们的列表中被认为是一个非常低的优先级,并且可能永远不会被修复。

以下是简单的重现步骤:

using System.Collections;

class C
{
    static void Main()
    {
        object[] array = { };
        IList list = new ArrayList();
        list.CopyTo(array, 0); // Works okay
        dynamic index = 0;
        list.CopyTo(array, index); // Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'System.Collections.IList' does not contain a definition for 'CopyTo'
    }
}

这里是问题的解释。当在静态类型为接口类型的表达式上调用函数成员(方法或索引器)时,调用的至少一个参数是 dynamic 类型(这意味着完整的成员查找-- 类型推断 -- 重载解析过程被推迟到运行时,并成为运行时绑定器而不是编译器的责任;编译器仅根据不完整的类型信息执行部分检查)和被调用的成员由接口从其基接口之一继承(而不是在接口本身中声明),则运行时绑定器无法正确遍历基接口树以找到继承的成员,并在运行时抛出异常,报告找不到所需的成员。请注意,这只是运行时绑定程序的错误——编译器正确地接受了调用(但会拒绝它,例如,如果您在方法名称中输入错误)。

可能的解决方法:将您调用成员的表达式强制转换为实际声明您尝试调用的成员的基接口。例如,上述重现步骤中的程序可以修复如下:

using System.Collections;

class C
{
    static void Main()
    {
        object[] array = { };
        IList list = new ArrayList();
        list.CopyTo(array, 0); // Works okay
        dynamic index = 0;
        ((ICollection) list).CopyTo(array, index); // Works okay
    }
}

或者,如果可能,通过将类型 dynamic 的参数转换为调用成员签名中指定的类型来完全摆脱动态分派。

using System.Collections;

class C
{
    static void Main()
    {
        object[] array = { };
        IList list = new ArrayList();
        list.CopyTo(array, 0); // Works okay
        dynamic index = 0;
        list.CopyTo(array, (int) index); // Works okay
    }
}

不幸的是,如果您确实希望在运行时进行重载解析,那么这两种解决方法都可能没有帮助,并且在可能的候选者中,既有接口声明的成员,也有接口继承的成员。在这种情况下,您可能需要发明一些临时解决方案,或者显着重构您的程序。