动态列表<t>。添加抛出RuntimeBinderException </t>

时间:2014-11-26 20:02:39

标签: c# dynamic reflection

我正在为OrientDB .Net库编写一些扩展方法,在C#中的模型类和数据库中的图之间进行映射(反之亦然)。这必然需要一些反思和动态编程。

以下方法用于设置模型对象上属性的值,该值表示顶点之间的边。例如,如果顶点A链接到具有边C的多个顶点B,则模型A可以具有类型List<B>的属性,而模型B具有类型A的属性(对于一个 - 多对多的关系。)

private static void SetLinkedProperty(
    ABaseModel parent, ABaseModel child, string className)
{
    PropertyInfo[] properties = parent.GetType()
        .GetProperties(BindingFlags.Public |
                       BindingFlags.Instance |
                       BindingFlags.SetProperty |
                       BindingFlags.GetProperty);
    PropertyInfo propertySingle = properties
        .Where(prop => IsModelProperty(prop, className)).SingleOrDefault();
    PropertyInfo propertyCollection = properties
        .Where(prop => IsModelCollectionProperty(prop, className)).SingleOrDefault();
    if (propertySingle != null)
    {
        propertySingle.SetValue(parent, child);
    }
    if (propertyCollection != null)
    {
        dynamic propertyValue = propertyCollection.GetValue(parent);
        if (propertyValue == null)
        {
            Type listOfT = typeof(List<>).MakeGenericType(
                propertyCollection.PropertyType.GenericTypeArguments[0]);
            IEnumerable collection = (IEnumerable)Activator.CreateInstance(listOfT);
            propertyValue = collection;
            propertyCollection.SetValue(parent, collection);
        }

        propertyValue.Add(child);
    }
}

模型中的属性可以具有为其提供别名的属性,以帮助在DB和C#类之间进行映射,因此IsModelPropertyIsModelCollectionProperty会检查该别名以及属性类型是否为可枚举的类型。

但是,当我运行代码时,我在第RuntimeBinderException行获得了propertyValue.Add(child)

  

OrientTest.exe中出现未处理的“Microsoft.CSharp.RuntimeBinder.RuntimeBinderException”类型异常

     

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

异常时:

  • parentOrientTest.Employer
  • 的一个实例
  • childOrientTest.Participant
  • 的一个实例
  • className是“EmployerParticipant”(在数据库中将雇主和参与者顶点链接在一起的边缘类的名称)
  • properties包含7个元素,每个元素对应Employer
  • 中的一个属性
  • propertySinglenull
  • propertyCollection代表属性List<Participant> Participants
  • propertyValueList<Participant>
  • 的一个实例

我不明白为什么List<Participant>#Add(Participant)有无效的参数,但dynamic经常做一些奇怪的事情。

1 个答案:

答案 0 :(得分:3)

重载解析失败,因为child的类型为ABaseModel,而非OrientTest.Participant。它在运行时的值恰好是方法所期望的类型并不重要。这似乎是反直觉的,因为名称RuntimeBinder,但其中有意义:重载决策的规则虽然在运行时应用,但与编译时C#使用的规则相同(因为{{ 1}}实际上是一个普通的旧C#对象)。偏离这一点会带来更多惊喜。

如果您编写自己的dynamic实现,当然可以覆盖或绕过此行为,因此这不是对DynamicObject的一般限制 - 它只是意味着您不能(ab)使用dynamic以这种方式在运行时进行方法解析。

在这种情况下,如果您知道该属性对于某些dynamic始终属于List<T>类型,则会有一个简单的修复,因为T实现List<T>接受IList任何旧的object(可能有运行时异常):

IList propertyValue = (IList) propertyCollection.GetValue(parent);
...
propertyValue.Add(child);

如果您不知道它是一个列表,那么您必须咬紧牙关并动态调用Add方法:

object propertyValue = propertyCollection.GetValue(parent);
...
propertyValue.GetType().GetMethod("Add").Invoke(propertyValue, child);

如果对象有多个.Add()方法,你想要使用&#34;最正确的&#34;一。我假设我们不需要涵盖那个特定的案例。

实际上有第三种方法,这种做法过于苛刻,但在其他情况下可能会有用,那就是让论证本身dynamic因此决议被迫做出正确的事情&#34 ;在运行时(对于某些值&#34;对&#34;):

propertyValue.Add((dynamic) child);