使用动态列表调用foreach内部的方法会导致RuntimeBinderException"不包含' Add'"

时间:2017-01-31 01:40:48

标签: c# list dynamic

我想了解动态关键字在以下代码中的工作原理。我发现了一些帖子,讨论了如何在IList上调用Add()导致RuntimeBinderException,但是没有解释为什么以下代码不起作用:

    [TestFixture]
public class TestDynamicOfIList
{
    [Test]
    public void Test1()
    {
        dynamic d1 = new ClassA();
        dynamic d2 = new ClassA();

        List<dynamic> dynamicList = new List<dynamic>();

        dynamicList.Add(d1);
        dynamicList.Add(d2);

        ClassGroup group = new ClassGroup();

        foreach (dynamic dynamicClassA in dynamicList)
        {
            // using var will fail during runtime! Need to use explicit type
            var sameObject = DoNothing(dynamicClassA);
            group.ClassAList.Add(sameObject);
        }
    }

    private ClassA DoNothing(dynamic classA)
    {
        return classA;
    }

    public class ClassA
    {
    }

    public class ClassGroup
    {
        public IList<ClassA> ClassAList { get;  } = new List<ClassA>();
    }
}

}

在上面的代码中,ClassGroup.ClassAList属性有一个已定义的类&Class;&#39; ClassA&#39;对于通用的IList&lt;&gt;,我不明白为什么编译器无法进行类型检查,也没有在运行时失败。

这里有几个我在StackOverflow上发现的类似问题:

How to create a List with a dynamic object type C#

Does not contain a definition for "Add"

为什么当我使用var sameObject时,它无效,但使用ClassA sameObject它可以正常工作?似乎认为sameObject变量的类型当方法DoNothing()明确返回ClassA

时,它是动态的

2 个答案:

答案 0 :(得分:2)

This question回答了编译器确定sameObjectdynamic的原因。

一个更简单的例子来证明这一点:

dynamic d1 = new ClassA();
var s = DoNothing(d1);
IList<ClassA> c = new List<ClassA>();
c.Add(s);

Add IList不起作用的原因似乎归结为重载解决方案。运行时无法在Add上找到要使用的IList重载,因此会抛出异常。您已经知道,如果您明确说明它的工作类型,那么这是解决问题的可能方法。

有趣的是,如果您使用ICollection<ClassA>(建议here),它确实有效,因此IList界面似乎存在问题:

dynamic d1 = new ClassA();
var s = DoNothing(d1);
ICollection<ClassA> c = new List<ClassA>();
c.Add(s);

答案 1 :(得分:-1)

  

我真的想知道为什么使用“var sameObject”不起作用

您正试图在Add上致电IList,这就是为什么它无效。您可以通过两种方式解决问题:

修复1

调用ToList()然后添加对象。

group.ClassAList.ToList().Add(sameObject);

修复2

从您的财产返回List

public class ClassGroup
{
    public List<ClassA> ClassAList { get;  } = new List<ClassA>();
}