反射和继承问题c#
Reflection and inheritance issue c#
我正在尝试使用反射来确定结构所有部分的 MemberExpression。这些是说明问题的一些对象:
public class Entity
{
public Part FirstPart { get; set; }
}
public class Part
{
public int Id { get; set; }
}
public class SubPart : Part
{
public int ExtraProperty { get; set; }
}
我用来确定每个组件的 MemberExpression 的函数适用于以下对象结构:
Entity entity = new Entity() { FirstPart = new Part() { Id = 1 } };
函数为:
var param = Expression.Parameter(entity.GetType());
String[] childProperties = ("FirstPart.Id").Split('.');
var propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
但是由于继承,这对以下对象不起作用:
Entity entity = new Entity() { FirstPart = new SubPart() { ExtraProperty = 1 } };
为了追溯属性,我们需要将路径更改为 "FirstPart.ExtraProperty":
var param = Expression.Parameter(entity.GetType());
String[] childProperties = ("FirstPart.ExtraProperty").Split('.');
var propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
错误消息指出:'ExtraProperty' 不是 Part 的成员。有谁知道如何解决这个问题?
你不能。将表达式视为代码,它是在运行时而不是编译时编译的。没有魔法和类似的规则适用(表达式是低级的且限制性更强,因此许多在 C# 代码级别可用的语法糖在表达式中不可用)。也就是说,由于 entity.FirstPart.ExtraProperty
在 C# 代码中无效,因此在表达式中也无效。
您可以插入显式强制转换 - 但是您假设实例实际上是 SubPart
类型,那么为什么不定义 SubPart
类型的成员 FirstPart
而不是Part
。或者,您可以使用 TypeIs expression 创建类型测试逻辑,然后按照与在 C# 代码中相同的方式进行转换。
编辑:
重读你的问题后,我发现你实际上想实现的是 属性 遍历任意对象。所以 TypeIs
表达式在这里对你没有帮助,因为它需要你正在测试的类型在编译时是已知的。但是在您的情况下, FirstPart
成员中的 Part
可以有任意的 class 派生,具有任意附加属性。在这种情况下,没有其他选择,只能逐一评估每个 属性 访问并从中间值中检索实际类型。例如:
Entity entity = new Entity() { FirstPart = new SubPart() { ExtraProperty = 1 } };
object currentObjectInChain = entity;
String[] childProperties = ("FirstPart.ExtraProperty").Split('.');
foreach (var property in childProperties)
{
if (currentObjectInChain == null)
{
throw new ArgumentException("Current value is null");
}
var type = currentObjectInChain.GetType();
var param = Expression.Parameter(type);
var lambda = Expression.Lambda(
Expression.PropertyOrField(param, property),
param).Compile(); // cache based on type and property name
currentObjectInChain = lambda.DynamicInvoke(currentObjectInChain);
}
在循环结束时 currentObjectInChain
将保留您的值。
我正在尝试使用反射来确定结构所有部分的 MemberExpression。这些是说明问题的一些对象:
public class Entity
{
public Part FirstPart { get; set; }
}
public class Part
{
public int Id { get; set; }
}
public class SubPart : Part
{
public int ExtraProperty { get; set; }
}
我用来确定每个组件的 MemberExpression 的函数适用于以下对象结构:
Entity entity = new Entity() { FirstPart = new Part() { Id = 1 } };
函数为:
var param = Expression.Parameter(entity.GetType());
String[] childProperties = ("FirstPart.Id").Split('.');
var propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
但是由于继承,这对以下对象不起作用:
Entity entity = new Entity() { FirstPart = new SubPart() { ExtraProperty = 1 } };
为了追溯属性,我们需要将路径更改为 "FirstPart.ExtraProperty":
var param = Expression.Parameter(entity.GetType());
String[] childProperties = ("FirstPart.ExtraProperty").Split('.');
var propExpression = Expression.PropertyOrField(param, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
propExpression = Expression.PropertyOrField(propExpression, childProperties[i]);
}
错误消息指出:'ExtraProperty' 不是 Part 的成员。有谁知道如何解决这个问题?
你不能。将表达式视为代码,它是在运行时而不是编译时编译的。没有魔法和类似的规则适用(表达式是低级的且限制性更强,因此许多在 C# 代码级别可用的语法糖在表达式中不可用)。也就是说,由于 entity.FirstPart.ExtraProperty
在 C# 代码中无效,因此在表达式中也无效。
您可以插入显式强制转换 - 但是您假设实例实际上是 SubPart
类型,那么为什么不定义 SubPart
类型的成员 FirstPart
而不是Part
。或者,您可以使用 TypeIs expression 创建类型测试逻辑,然后按照与在 C# 代码中相同的方式进行转换。
编辑:
重读你的问题后,我发现你实际上想实现的是 属性 遍历任意对象。所以 TypeIs
表达式在这里对你没有帮助,因为它需要你正在测试的类型在编译时是已知的。但是在您的情况下, FirstPart
成员中的 Part
可以有任意的 class 派生,具有任意附加属性。在这种情况下,没有其他选择,只能逐一评估每个 属性 访问并从中间值中检索实际类型。例如:
Entity entity = new Entity() { FirstPart = new SubPart() { ExtraProperty = 1 } };
object currentObjectInChain = entity;
String[] childProperties = ("FirstPart.ExtraProperty").Split('.');
foreach (var property in childProperties)
{
if (currentObjectInChain == null)
{
throw new ArgumentException("Current value is null");
}
var type = currentObjectInChain.GetType();
var param = Expression.Parameter(type);
var lambda = Expression.Lambda(
Expression.PropertyOrField(param, property),
param).Compile(); // cache based on type and property name
currentObjectInChain = lambda.DynamicInvoke(currentObjectInChain);
}
在循环结束时 currentObjectInChain
将保留您的值。