2015-07-14 3 views
2

Мне нужно программно распознать, когда индексир встречается внутри выражения, но результирующее дерево выражений не то, что я ожидал.Признать индексатора в выражении LINQ

class IndexedPropertiesTest 
{ 
    static void Main(string[] args) { new IndexedPropertiesTest(); } 

    public string this[int index] 
    { 
     get { return list[index]; } 
     set { list[index] = value; } 
    } 
    List<string> list = new List<string>(); 

    public IndexedPropertiesTest() 
    { 
     Test(() => this[0]); 
    } 

    void Test(Expression<Func<string>> expression) 
    { 
     var nodeType = expression.Body.NodeType; 
     var methodName = ((MethodCallExpression)expression.Body).Method.Name; 
    } 
} 

В приведенном выше коде, nodeType является "Call" и methodName является "get_Item". Зачем? Не должно быть expression.Body эквивалентно Expression.Property(Expression.Constant(this), "Item", Expression.Constant(0))? Это то, чего я ожидал.

Мне нужна способность обнаруживать индексатор в очень общем виде - дается практически любое выражение. Это искажение дерева предполагаемого выражения компрометирует мою способность делать это. Опираясь на название метода, «get_Item» слишком хрупкое. Плюс, IndexerNameAttribute, возможно, использовался для переименования свойства indexer.

Так или иначе, чтобы получить компилятор для генерации целевого дерева выражений? Не предлагайте вручную создавать выражение, поскольку это не вариант. Или есть ли способ программно убедиться, что у меня есть индекс?

+2

'Мне нужно программно определить, когда индексатор происходит внутри expression' вы уверены? Потому что не все языки .NET даже имеют конструкцию, называемую индексом. Вот почему метод get_Item создается внутри. Что бы вы ожидали, если ваш код вызывается из VB.NET? – nvoigt

+0

@nvoigt VB.NET имеет * несколько указателей *, которые я бы хотел, чтобы мой код также распознал. Итак, для langauges, у которых * есть * есть индексы, почему компилятор не генерирует «IndexExpression»? Каким образом языки, у которых нет указателей, важны для этого вопроса? Я думаю, что важно то, что основная модель отражения включает индексированные свойства, как показано на [Property.GetValue] (https://msdn.microsoft.com/en-us/library/b05d59ty.aspx). – HappyNomad

ответ

1

Думаю, у вас есть свой ответ: так работает компилятор C#.

Я перевел ваш код на VB.NET. Недостаточно, VB.NET не вызывает метод get_Item, скорее он называет его именем, которое вы ему даете. В приведенном ниже примере это заканчивается get_MyDefaultProperty.

Sub Main 
    Dim x as IndexedPropertiesTest = New IndexedPropertiesTest() 

End Sub 

' Define other methods and classes here 
Class IndexedPropertiesTest 

    Private list as New List(Of String) From { "a" } 
    Default Property MyDefaultProperty(index as Integer) as String 
     Get 
      Return list(index) 
     End Get 
     Set(value as String) 
      list(index) = value 
     End Set 
    End Property 

    Public Sub New 
     Test(Function() Me(0)) 
    End Sub 

    Public Sub Test(expression as Expression(Of Func(Of String))) 

     Dim nodeType as ExpressionType = expression.Body.NodeType 
     Dim methodName as String = CType(expression.Body, MethodCallExpression).Method.Name 

     'expression.Dump() 'Using LINQPad 

    End Sub 

End Class 

Однако, не все потеряно: Вы можете написать Visitor, чтобы попытаться запихнуть get_Item вызов обратно в IndexExpression. Я начал на него здесь:

public class PropertyFixerVisitor : ExpressionVisitor 
{ 
    protected override Expression VisitMethodCall(MethodCallExpression node) 
    { 
     if (node.Method.Name.StartsWith("get_")) 
     { 
      var possibleProperty = node.Method.Name.Substring(4); 
      var properties = node.Method.DeclaringType.GetProperties() 
       .Where(p => p.Name == possibleProperty); 

      //HACK: need to filter out for overriden properties, multiple parameter choices, etc. 
      var property = properties.FirstOrDefault(); 
      if (property != null) 
       return Expression.Property(node.Object, possibleProperty, node.Arguments.ToArray()); 
      return base.VisitMethodCall(node); 

     } 
     else 
      return base.VisitMethodCall(node); 
    } 
} 

Вы можете безопасно модифицировать свой метод тестирования, как так:

void Test(Expression<Func<string>> expression) 
{ 
    var visitor = new PropertyFixerVisitor(); 
    var modExpr = (Expression<Func<string>>)visitor.Visit(expression); 

    var indexExpression = (modExpr.Body as IndexExpression); //Not Null 
} 
+1

Ваш код «Посетителя» помог мне преодолеть проблему. Единственное, чего не хватает, - это объяснение того, почему компилятор искажает индексы. Но, полагаю, на это может ответить только Microsoft. – HappyNomad

+0

Я был откровенен с любопытством. Я посмотрел, и [этот ответ] (http://stackoverflow.com/questions/3340500/how-does-one-create-a-net-expression-with-nodetype-of-expressiontype-index) вызывает подсказку: ['IndexExpression'] (https://msdn.microsoft.com/en-us/library/system.linq.expressions.indexexpression.aspx) не существовало в .NET 3.5, это было одно из многих выражений' Expression' классы добавлены в 4.0. Эти добавленные классы выражений (например, AssignExpression), которые никогда не интегрировались в компилятор. Например, вы не можете использовать назначение в выражении, вы должны его построить с помощью классов Expression. – Shlomo

Смежные вопросы