【问题标题】:Calling `Contains` method from C# dynamic type yields error - why?从 C# 动态类型调用“包含”方法会产生错误 - 为什么?
【发布时间】:2016-10-01 15:17:46
【问题描述】:

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

您可以将其复制粘贴到控制台应用程序中并亲自查看。

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();
    }
}

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

收到的实际错误:

System.Core.dll 中出现“Microsoft.CSharp.RuntimeBinder.RuntimeBinderException”类型的未处理异常

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

对于那些可能觉得相关的人,我添加了我的实际代码的 sn-p:

case RelationNavigation.RelationNavigationMultiplicity.ManyToOne:
{
    var stringValue = dto.PropertiesData[relationNavigation.PropertyEnd.PropertyInfo.Name];
    if (string.IsNullOrEmpty(stringValue))
        continue;

    dynamic list = relationNavigation.PropertyEnd.PropertyInfo.GetValue(modelObj);

    var relatedModelIds = stringValue.Split(',').Select(int.Parse).ToArray();
    foreach (var relatedModelId in relatedModelIds)
    {
        var relatedModel = GetById(relationNavigation.PropertyEnd.RelatedType, relatedModelId);
        if (relatedModel == null)
            continue;

        if(!list.GetType().GetMethod("Contains").Invoke(list, new[] { relatedModel }))
        //if (!list.Contains(relatedModel))
            list.Add(relatedModel);
        relationNavigation.RelatedObjectPropertyEnd.PropertyInfo.SetValue(relatedModel, modelObj);
    }
    break;
}

谢谢。

【问题讨论】:

  • @sstan 将添加..
  • 什么类型在运行时有 obj 和 z?我知道它不会被修复,只是当您遇到异常时这些类型的一个示例。
  • 您可以在这里找到问题的解释:Method not being resolved for dynamic generic type。它还包括解决方案,即将z 转换为动态:if (x.Contains((dynamic)z))。但就其价值而言,最好尽可能寻找非动态解决方案。
  • 我编辑了我的答案你看了吗? :)

标签: c# .net dynamic reflection


【解决方案1】:

而不是

if (x.Contains(z))

您需要将z 转换为dynamic

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

有关为什么需要这样做的更多信息,请参阅此处:Method not being resolved for dynamic generic type,我认为此问题与此问题重复。

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

【讨论】:

  • @G.Y ???我不明白你为什么接受这个答案。古斯曼早些时候告诉过你同样的事情。 --- 但我不必什么都懂。
  • @Verarind 这个答案更准确地解决了我提出的问题。但不用担心,我会确保以其他方式提升古斯曼的声望;)
【解决方案2】:

您将函数参数定义为对象,如果您将参数更改为也是动态的,它将起作用:

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"));

    }

}

【讨论】:

  • 但是我需要有对象——因为在实际程序中,对象是从返回一个对象的 Reflection-GetValue 获得的。
  • 如果参数是动态的,你可以传递任何东西,这就是动态的含义:)
  • @Gusman:我认为你误解了 OP 的问题。 OP 在运行时而不是在编译时得到错误。 obj 在调用 Contains 之前被强制转换为 x,这是动态的。所以我认为你的答案在这里不适用。
  • 好吧,他在编译的CS错误之前添加了它,它已被删除......无论如何,这样做它有效,我将在这里添加一个测试程序,我曾经检查过这个会工作。
  • @GY 好的,检查了它,我明白了,buuuttt .. 工作...检查这个粘贴箱,它有一个示例程序正在做你正在做的事情,你一定有一个错误关于对象的类型:pastebin.com/XCh82mhm
【解决方案3】:

使用 as 运算符,也许你想检查 x 是否为空。

    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();
        }
    }

【讨论】:

  • List&lt;MyDataClass&gt; 在运行时未知.. 不能在 MyOperationsClass 中使用它
  • 但它不一定是一个列表。在我的真实程序中,真正的类型是Hashset&lt;T&gt;,它不是派生IList,但它确实实现了Contains(T Item)方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多