如何让孩子从表达式中声明类型?
我有一个父/子类层次结构中的父抽象声明一个字符串属性和子类实现它:如何让孩子从表达式中声明类型?
abstract class Parent
{
public abstract string Value { get; }
}
class Child : Parent
{
public override string Value { get { return null; } }
}
当我使用显式(或隐式)使用儿童类的表情,我期待表达的的MemberInfo的DeclaringType是“孩子”,而是它是父:
Child child = new Child();
Expression<Func<string>> expression = (() => child.Value);
MemberInfo memberInfo = expression.GetMemberInfo();
Assert.AreEqual(typeof(Child), memberInfo.DeclaringType); // FAILS!
因为DeclaringType是家长断言失败。
有什么我可以做声明我的表达或消耗它来揭示儿童类型的实际使用?
注:GetMemberInfo()上面的扩展方法:
public static class TypeExtensions
{
/// <summary>
/// Gets the member info represented by an expression.
/// </summary>
/// <param name="expression">The member expression.</param>
/// <returns>The member info represeted by the expression.</returns>
public static MemberInfo GetMemberInfo(this Expression expression)
{
var lambda = (LambdaExpression)expression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else memberExpression = (MemberExpression)lambda.Body;
return memberExpression.Member;
}
}
否 - 这是什么得到由C#编译器发出准确的表示(我甚至忘了我们这样写的!)。当寻找成员时,覆盖被忽略 - 编译器只关心最初声明为成员的类型。您可以通过编译代码然后查看IL来看到这一点。这种方法:
static void Main()
{
Child c = new Child();
string x = c.Value;
}
被编译成这个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编译不相同的方式工作,所以这种方法:
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
哇,谢谢你的详细回复!我想这意味着:“这是不可能的”,所以我需要回到制图板。我的实际目标是GetCustomAttributes(),但是一些自定义属性被添加到子类中的重写属性,但使用上述我无法联系他们,因为DeclaringType是Parent。 – Trinition 2012-02-27 14:30:58
@Trinition:但是你可以得到成员引用的* target *的类型,即'child'的类型。这不够好吗?由于您的示例代码无效,因此很难确切知道显示的内容 - “表达式
我编辑并添加了上面的GetMemberInfo()的主体。 – Trinition 2012-02-27 14:40:09
如果你不想使用静态类型的方法,而是使用最新的重写,那么这是可能的。我没有测试,但是类似于以下的东西应该做的工作:
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);
}
类'Child'不从'Parent'继承! – vulkanino 2012-02-27 14:25:11
什么是GetMemberInfo()方法?如果是扩展名,请发布其实施。 – Ani 2012-02-27 14:27:38
“如果从中获取此MemberInfo对象的Type对象未声明此成员,则DeclaringType属性将表示其基本类型之一。” – vulkanino 2012-02-27 14:29:23