C# 如何从表达式中获取子声明类型?
我有一个父/子类层次结构,父类抽象声明一个字符串属性,子类实现它:C# 如何从表达式中获取子声明类型?,c#,.net,linq,lambda,C#,.net,Linq,Lambda,我有一个父/子类层次结构,父类抽象声明一个字符串属性,子类实现它: abstract class Parent { public abstract string Value { get; } } class Child : Parent { public override string Value { get { return null; } } } 当我使用显式(或隐式)使用子类的表达式时,我希望表达式的MemberInfo的DeclaringType为“Child”,但它是父
abstract class Parent
{
public abstract string Value { get; }
}
class Child : Parent
{
public override string Value { get { return null; } }
}
当我使用显式(或隐式)使用子类的表达式时,我希望表达式的MemberInfo的DeclaringType为“Child”,但它是父类:
Child child = new Child();
Expression<Func<string>> expression = (() => child.Value);
MemberInfo memberInfo = expression.GetMemberInfo();
Assert.AreEqual(typeof(Child), memberInfo.DeclaringType); // FAILS!
Child-Child=new-Child();
表达式=(()=>child.Value);
MemberInfo MemberInfo=expression.GetMemberInfo();
Assert.AreEqual(typeof(Child),memberInfo.DeclaringType);//失败!
断言失败,因为DeclaringType是父类型
在声明或使用表达式以揭示子类型的实际使用时,我可以做些什么
注意:上面的GetMemberInfo()是一个扩展方法(我甚至忘了我们写过这个!)
公共静态类类型扩展
{
///
///获取由表达式表示的成员信息。
///
///成员表达式。
///表达式表示的成员信息。
公共静态MemberInfo GetMemberInfo(此表达式)
{
var lambda=(LambdaExpression)表达式;
成员表达成员表达;
if(lambda.Body是一元表达式)
{
var unaryExpression=(unaryExpression)lambda.Body;
memberExpression=(memberExpression)unaryExpression.Operator;
}
else memberExpression=(memberExpression)lambda.Body;
返回memberExpression.Member;
}
}
否-这是C#编译器发出内容的准确表示。查找成员时,重写实际上被忽略-编译器只关心最初声明该成员的类型。通过编译代码,然后查看IL,您可以自己看到这一点。此方法:
static void Main()
{
Child c = new Child();
string x = c.Value;
}
Public Shared Sub Main(Args As String())
Dim x As Child = New Child()
Dim y As String = x.Value
End Sub
已编译成此IL:
IL_0000: nop
IL_0001: newobj instance void Child::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt instance string Parent::get_Value()
IL_000d: stloc.1
IL_000e: ret
一点小技巧:VB编译器的工作方式不同,因此此方法:
static void Main()
{
Child c = new Child();
string x = c.Value;
}
Public Shared Sub Main(Args As String())
Dim x As Child = New Child()
Dim y As String = x.Value
End Sub
汇编如下:
IL_0000: newobj instance void [lib]Child::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: callvirt instance string [lib]Child::get_Value()
IL_000c: stloc.1
IL_000d: ret
如果您不希望使用您处理的静态类型的方法,而是希望使用最新的重写,那么这是可能的。我没有进行测试,但类似于以下内容的内容应该可以完成这项工作:
public bool FindOverride(MethodInfo baseMethod, Type type)
{
if(baseMethod==null)
throw new ArgumentNullException("baseMethod");
if(type==null)
throw new ArgumentNullException("type");
if(!type.IsSubclassOf(baseMethod.ReflectedType))
throw new ArgumentException(string.Format("Type must be subtype of {0}",baseMethod.DeclaringType));
while(true)
{
var methods=type.GetMethods(BindingFlags.Instance|
BindingFlags.DeclaredOnly|
BindingFlags.Public|
BindingFlags.NonPublic);
var method=methods.FirstOrDefault(m=>m.GetBaseDefinition()==baseMethod))
if(method!=null)
return method;
type=type.BaseType;
}
}
其中,将
MemberInfo
作为第一个参数传递,将对象的运行时类型作为第二个参数传递。请注意,这可能很慢,因此您可能需要添加一些缓存。基于@JonSkeet和@CodeInChaos提供的信息,我的解决方案不是单纯查看表达式中的PropertyInfo,而是查看MemberExpression成员组件的类型:
/// <summary>
/// Extracts the PropertyInfo for the propertybeing accessed in the given expression.
/// </summary>
/// <remarks>
/// If possible, the actual owning type of the property is used, rather than the declaring class (so if "x" in "() => x.Foo" is a subclass overriding "Foo", then x's PropertyInfo for "Foo" is returned rather than the declaring base class's PropertyInfo for "Foo").
/// </remarks>
/// <typeparam name="T"></typeparam>
/// <param name="propertyExpression"></param>
/// <returns></returns>
internal static PropertyInfo ExtractPropertyInfo<T>(Expression<Func<T>> propertyExpression)
{
if (propertyExpression == null)
{
throw new ArgumentNullException("propertyExpression");
}
var memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression == null)
{
throw new ArgumentException(string.Format("Expression not a MemberExpresssion: {0}", propertyExpression), "propertyExpression");
}
var property = memberExpression.Member as PropertyInfo;
if (property == null)
{
throw new ArgumentException(string.Format("Expression not a Property: {0}", propertyExpression), "propertyExpression");
}
var getMethod = property.GetGetMethod(true);
if (getMethod.IsStatic)
{
throw new ArgumentException(string.Format("Expression cannot be static: {0}", propertyExpression), "propertyExpression");
}
Type realType = memberExpression.Expression.Type;
if(realType == null) throw new ArgumentException(string.Format("Expression has no DeclaringType: {0}", propertyExpression), "propertyExpression");
return realType.GetProperty(property.Name);
}
//
///为给定表达式中正在访问的属性提取PropertyInfo。
///
///
///如果可能,将使用属性的实际拥有类型,而不是声明类(因此,如果“()=>x.Foo”中的“x”是覆盖“Foo”的子类,则返回“Foo”的x的PropertyInfo,而不是“Foo”的声明基类的PropertyInfo)。
///
///
///
///
内部静态属性Info ExtractPropertyInfo(表达式属性Expression)
{
if(propertyExpression==null)
{
抛出新ArgumentNullException(“propertyExpression”);
}
var memberExpression=propertyExpression.Body作为memberExpression;
if(memberExpression==null)
{
抛出新的ArgumentException(string.Format(“表达式不是MemberExpression:{0}”,propertyExpression),“propertyExpression”);
}
var property=memberExpression.Member作为PropertyInfo;
if(属性==null)
{
抛出新的ArgumentException(string.Format(“表达式不是属性:{0}”,propertyExpression),“propertyExpression”);
}
var getMethod=property.getMethod(true);
if(getMethod.IsStatic)
{
抛出新ArgumentException(string.Format(“表达式不能是静态的:{0}”、propertyExpression)、“propertyExpression”);
}
Type realType=memberExpression.Expression.Type;
如果(realType==null)抛出新的ArgumentException(string.Format(“表达式没有DeclaringType:{0}”),则为“propertyExpression”);
返回realType.GetProperty(property.Name);
}
类子类
不从父类继承!这个GetMemberInfo()方法是什么?如果它是一个扩展,请发布它的实现。“如果从中获取此MemberInfo对象的类型对象没有声明此成员,DeclaringType属性将表示其基本类型之一。”vulkanino,这是我的一个输入错误。谢谢你抓住它!您要求声明类型,这就是您得到的。该成员由基类声明。它被派生类重写,但重写不算作声明新的“方法槽”;而是专门针对现有的插槽。哇,感谢您的详细回复!我猜这意味着:“这是不可能的”,所以我需要回到绘图板。我的实际目标是获取CustomAttributes(),但有些自定义属性被添加到子类中的重写属性中,但使用上述属性,我无法访问它们,因为DeclaringType是父类。@Trinition:但您可以获取成员引用的目标类型,即子类的类型。那还不够好吗?由于示例代码无效,很难确切地知道要显示什么-在Expression
@Trinition上没有这样的GetMemberInfo
方法:对,一旦你有了MemberExpression
,你就可以使用MemberExpression.Expression.Type
。即使VB.NET编译器的工作方式不同,VB.NET中的表达式与C#--Dim expr中的表达式具有相同的行为,因为表达式(Of Func(Of Child,String))=Function(x)x.Value:CType(expr.Body,MemberExpression)。Member getype(Child)。GetMember(“Value”)(0)
@ZevSpitz我刚刚做了一个小动作()证明它们实际上没有相同的行为。您是否在特定的运行时版本上运行此操作?还是我错过了什么?这很有效,谢谢。我是abl