从C#动态类型调用`Contains`方法会产生错误 - 为什么?

时间:2016-06-02 04:06:36

标签: c# .net dynamic reflection

我将我的问题简化为一个小程序,它说明了我在运行时收到的确切错误。

您可以将其复制粘贴到控制台应用程序中,然后亲眼看看。

using System.Collections.Generic;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<MyDataClass>()
            {
                new MyDataClass {A = 1, B = 1},
                new MyDataClass {A = 3, B = 8}
            };
            var ops = new MyOperationsClass();
            ops.ReallyGreatOperation(list, list[0]);
        }
    }

    public class MyDataClass
    {
        public int A { get; set; }
        public int B { get; set; }

        public void AddDataPartsToEachOther()
        {
            var c = A + B;
            A = c;
            B = c;
        }
    }

    public class MyOperationsClass
    {
        public void ReallyGreatOperation(object obj, object z)
        {
            dynamic x = obj;
            if (x.Contains(z)) //<-- gets an error here..
                ((dynamic)z).AddDataPartsToEachOther();
        }
    }
}

那真正的问题是什么?

据我所知,dynamic关键字可以用作通配符,如果存在方法,它将被调用而没有问题。那么为什么在这种情况下它不适合我?

现在,我知道我可以通过这样做来改变它:

public class MyOperationsClass
{
    public void ReallyGreatOperation(object obj, object z)
    {
        dynamic x = obj;
    //    if (x.Contains(z)) //<-- gets an error here..
    //        ((dynamic)z).AddDataPartsToEachOther();
        if (x.GetType().GetMethod("Contains").Invoke(obj, new[] {z}))
            ((dynamic)z).AddDataPartsToEachOther();
    }
}

但正如我所说 - 我希望理解为什么更“自然”的方式不起作用..因为如果我这样做是第二种方式别无选择 - 我没有看到{{1再用语言了。

收到的实际错误:

System.Core.dll中发生未处理的“Microsoft.CSharp.RuntimeBinder.RuntimeBinderException”异常

附加信息:'System.Collections.Generic.List.Contains(ConsoleApplication5.MyDataClass)'的最佳重载方法匹配有一些无效的参数

对于那些可能发现它相关的人,我添加了一段实际代码:

dynamic

感谢。

3 个答案:

答案 0 :(得分:2)

而不是

if (x.Contains(z))

您需要将z投射到dynamic

if (x.Contains((dynamic)z))

有关为何需要这些内容的详细信息,请参阅此处:Method not being resolved for dynamic generic type,我认为这个问题是重复的。

那就是说,使用dynamic通常是糟糕的设计。使用泛型和/或接口通常有更好的方法。

答案 1 :(得分:1)

您将函数参数定义为对象,如果将参数更改为动态参数,则可以使用:

public void ReallyGreatOperation(dynamic obj, dynamic z)
{
    if (obj.Contains(z))
        z.AddDataPartsToEachOther();
}

这是一个非常小的测试程序:

class Program
{
    static bool test(dynamic d, dynamic c)
    {
        return d.Contains(c);
    }

    static void Main(string[] args)
    {
        Console.WriteLine(test(new List<string>(), "not found"));

    }

}

答案 2 :(得分:0)

使用as运算符,可能要检查x是否为null。

    public class MyOperationsClass
    {
        public void ReallyGreatOperation(object obj, object z)
        {
            List<MyDataClass> x = obj as List<MyDataClass>;
            if (x.Contains(z))
                ((dynamic)z).AddDataPartsToEachOther();
        }
    }

修改 另一种方法是将对象转换为IList。

    public class MyOperationsClass
    {
        public void ReallyGreatOperation(object obj, object z)
        {
            IList list = obj as IList;
            if (list.Contains(z))
                ((dynamic)z).AddDataPartsToEachOther();
        }
    }