Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何从表达式中获取子声明类型?_C#_.net_Linq_Lambda - Fatal编程技术网

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