动态列表.添加抛出运行时绑定器异常
本文关键字:运行时 绑定 异常 添加 列表 动态 | 更新日期: 2023-09-27 18:32:13
我正在努力为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);
}
}
模型中的属性可以具有属性,为它们提供别名以帮助在数据库和 C# 类之间进行映射,因此IsModelProperty
和IsModelCollectionProperty
会检查该别名以及属性类型是否为可枚举类型。
但是,当我运行代码时,我在propertyValue.Add(child)
行得到RuntimeBinderException
:
OrientTest 中发生了类型为"Microsoft.CSharp.RuntimeBinder.RuntimeBinderException"的未处理异常.exe
附加信息:"System.Collections.Generic.List.Add(OrientTest.Participant)"的最佳重载方法匹配有一些无效参数
在异常点:
-
parent
是OrientTest.Employer
的一个实例 -
child
是OrientTest.Participant
的一个实例 -
className
为"雇主参与者"(在数据库中将雇主和参与者折点链接在一起的边类的名称) -
properties
包含 7 个元素,每个元素对应于Employer
中的一个属性 -
propertySingle
null
-
propertyCollection
表示属性List<Participant> Participants
-
propertyValue
是List<Participant>
的一个实例
我不明白为什么List<Participant>#Add(Participant)
有无效的论点,但dynamic
经常做奇怪的事情。
重载解析失败,因为child
的类型是ABaseModel
,而不是OrientTest.Participant
。它在运行时的值恰好是方法期望的类型并不重要。考虑到名称RuntimeBinder
,这似乎违反直觉,但它是有道理的:重载解析的规则虽然在运行时应用,但与 C# 在编译时使用的规则相同(因为dynamic
实际上是一个普通的旧 C# 对象)。偏离这一点会导致更多的惊喜。
当然,如果您编写自己的DynamicObject
实现,则可以覆盖或规避此行为,因此这不是对dynamic
的一般限制 - 它只是意味着您不能(滥用)使用dynamic
在运行时以这种方式进行方法解析。
在这种情况下,如果您知道该属性对于某些T
始终是类型 List<T>
,则有一个简单的修复,因为List<T>
实现了接受任何旧object
的IList
(可能存在运行时异常):
IList propertyValue = (IList) propertyCollection.GetValue(parent);
...
propertyValue.Add(child);
如果你不知道这是一个列表,你必须咬紧牙关,动态调用Add
方法:
object propertyValue = propertyCollection.GetValue(parent);
...
propertyValue.GetType().GetMethod("Add").Invoke(propertyValue, child);
如果对象有多个.Add()
方法,而您希望使用"最正确"的方法,则此操作又会失败。我假设我们不需要涵盖这种特殊情况。
实际上还有第三种方法,在这里是矫枉过正的,但在其他情况下可能很有用,那就是使参数本身dynamic
因此解决方案被迫在运行时执行"正确的事情"(对于"正确"的某些值):
propertyValue.Add((dynamic) child);