Extraer a los accesos miembros de una expresión en C # -- # campo con comparative-review camp codereview Relacionados El problema

Extracting member-accesses from an expression in C#


4
vote

problema

Español

Necesito extraer todos los accesos de miembros al parámetro de expresión (y asegúrese de que no estén anidados). Por ejemplo, para la expresión: io_context5 Necesito recibir la lista io_context6 . Para la expresión io_context7 se debe arrojar, ya que hemos anidado acceso a miembros.

Escribí la siguiente clase, pero no estoy demasiado satisfecho con eso. Principalmente porque no me gusta el patrón "Llamar, luego acceder a Miembro de acuerdo", porque disminuye la capacidad de mantenimiento del código y permite introducir los errores difíciles de encontrar (es decir, si puedo acceder a los resultados más adelante sin llamar, etc.)

Me pregunto si esta clase podría implementarse de tal manera, para que pueda llamar:

  io_context8  

Mi solución actual sigue:

  io_context9  

Llamada de ejemplo:

  run()0  

Editar: SAFER, pero con el campo de procesamiento "torposo".

  run()1  
Original en ingles

I need to extract all member accesses to expression parameter (and make sure, that they are not nested). For instance, for expression: a => a.A + a.B I need to receive list {"A", "B"}. For expression a => a.B.C exception should be thrown, as we have nested member access.

I wrote the following class, but I'm not too satisfied with it. Mainly because I don't like pattern "Call, then access member for result", because it decreases code maintainability and allows introducing hard to find bugs (ie. what if I accidentally access results later without calling etc.)

I'm wondering, if this class might be implemented in such way, so that I could call:

List<string> members = new MemberExpressionVisitor<A, bool>().GetMembers(a => a.A + a.B); 

My current solution follows:

private class MemberExpressionVisitor<T1, T2> : ExpressionVisitor {     private Expression<Func<T1, T2>> originalExpression = null;     private HashSet<string> accessedMembers = null;      public override Expression Visit(Expression node)     {         if (originalExpression == null)         {             if (node is Expression<Func<T1, T2>> startExpression)             {                 originalExpression = startExpression;                 accessedMembers = new HashSet<string>();                  var result = base.Visit(node);                  originalExpression = null;                  return result;             }             else             {                 throw new InvalidOperationException($"Passed expression must be of type Expression<Func<{typeof(T1).Name},{typeof(T2).Name}>>!");             }         }         else         {             return base.Visit(node);         }     }      protected override Expression VisitMember(MemberExpression node)     {         if (node.Expression is MemberExpression)             throw new InvalidOperationException("Nested member accesses are not allowed!");          if (node.Expression is ParameterExpression parameterExpression)         {             if (parameterExpression.Name == originalExpression.Parameters[0].Name)                 accessedMembers.Add(node.Member.Name);         }          return base.VisitMember(node);     }      public List<string> GetAccessedMembers() => accessedMembers.ToList();            } 

Example call:

public class B {     public int P => 5; }  public class A {     public B B => new B();     public int C => 8; }  static void Main(string[] args) {     Expression<Func<A, bool>> exp = a => a.B != null && a.C ==8;      MemberExpressionVisitor<A, bool> visitor = new MemberExpressionVisitor<A, bool>();     visitor.Visit(exp);      Console.WriteLine(String.Join(", ", visitor.GetAccessedMembers()));     Console.ReadKey(); } 

Edit: Safer, but with clumsy "processing" field.

private class MemberExpressionExtractor : ExpressionVisitor {     private bool processing = false;     private string parameterName;     private readonly HashSet<string> accessedMembers = new HashSet<string>();      public List<string> ExtractMembers<T1, T2>(Expression<Func<T1, T2>> expression)     {         try         {             processing = true;              parameterName = expression.Parameters[0].Name;             accessedMembers.Clear();              Visit(expression);         }         finally         {             processing = false;         }          return accessedMembers.ToList();     }      public override Expression Visit(Expression node)     {         if (!processing)             throw new InvalidOperationException("Use ExtractMembers<T1, T2> instead.");          return base.Visit(node);     }      protected override Expression VisitMember(MemberExpression node)     {         if (node.Expression is MemberExpression)             throw new InvalidOperationException("Nested member accesses are not allowed!");          if (node.Expression is ParameterExpression parameterExpression)         {             if (parameterExpression.Name == parameterName)                 accessedMembers.Add(node.Member.Name);         }          return base.VisitMember(node);     } } ``` 
     
 
 

Lista de respuestas

0
 
vote

¿Qué pasa si anuló el q9 clase dentro de otra clase? Dado que me gustan los métodos de extensión, cree una clase estática que contenga un método de extensión en match0 que crea el visitante, lo ejecuta y devuelve el resultado:

  match1  

Ahora puedes usarlo como:

  match2  

Alternativamente, puede agregar un constructor a match3 :

  match4  

Eliminar el match5 anular en su totalidad y simplifique el método de extensión para:

  match6  

Esto tiene la seguridad del tipo de aplicación del compilador, suponiendo que conoce sus tipos de variables de expresión de lambda correctamente.

 

What if you nested the MemberExpressionVisitor class inside another class? Since I like extension methods, create a static class that contains an extension method on Expression<Func<T,TRes>> that creates the visitor, runs it and returns the result:

static class LambdaExt {     public static List<string> GetAccessedMemberNames<T, TRes>(this Expression<Func<T, TRes>> startExpr) {         var v = new MemberExpressionVisitor<T, TRes>();         v.Visit(startExpr);          return v.GetAccessedMembers();     }      // put MemberExpressionVisitor definition here } 

Now you can use it like:

Console.WriteLine(String.Join(", ", exp.GetAccessedMemberNames())); 

Alternatively, you can add a constructor to MemberExpressionVisitor:

public MemberExpressionVisitor(Expression<Func<T1, T2>> startExpression) {     originalExpression = startExpression;     Visit(startExpression); } 

remove the Visit override entirely, and simplify the extension method to:

public static List<string> GetAccessedMemberNames<T, TRes>(this Expression<Func<T, TRes>> startExpr) =>     new MemberExpressionVisitor<T,TRes>(startExpr).GetAccessedMembers(); 

This has the compiler enforce type safety, assuming you know your lambda expression variables types properly.

 
 

Relacionados problema

18  Invirtiendo una cadena  ( Reversing a string ) 
Tuve esto como una pregunta de entrevista, y el entrevistador señaló esto. Esto es lo que escribí: //C# Syntax here public string Reverse(string s) { c...

5  Representando el tiempo de apertura y cierre para un negocio  ( Representing the opening and closing time for a business ) 
Tengo una clase de openclose que solo representa las horas de operación de un negocio por la apertura y el tiempo de cierre. Toma los tiempos de apertura y ci...

2  Pasando funtores a algoritmos estándar  ( Passing functors to standard algorithms ) 
He creado 3 implementaciones diferentes (que producen los mismos resultados) utilizando la función en3 de STL. Deseo verificar la cantidad de elementos en e...

1  Configuración del camino a la barra de herramientasSociadaxIname, con retraso  ( Setting the path to toolbarassociatedxibname with fallback ) 
Tengo algún código que he incluido en un proyecto que quiero liberar. Aquí hay uno de esos bloques: NSString *path = nil; if (delegate && [delegate respond...

1  Visualización de Wuuks completados y fallidos usando una o dos funciones  ( Displaying completed and failed wuuks using one or two functions ) 
He actualizado mi código. En el código antiguo tuve dos funciones: display_maker_success()1 y display_maker_fail() : function display_maker_success($lin...

2  Dos formas de aleatorias aleatoriamente las tarjetas  ( Two ways to randomly shuffle cards ) 
Aquí hay dos implementaciones que escribí para aleatorizar las tarjetas. El primer método ( dt5 ) Selecciona una tarjeta aleatoria, luego lo quita al frent...

2  Formateo de un precio en SWIFT usando una extensión vs. una clase  ( Formatting a price in swift using an extension vs a class ) 
Tengo un precio como un Double , que debería formatearlo como un String . ¿Debo usar una extensión en lugar de clase clásica para encapsular "Formater", "...

8  Contando novedades en un archivo  ( Counting newlines in a file ) 
He comenzado a pasar por tutoriales de nodos en nodoschool.io , y la segunda asignación fue escribir un programa que contará El número de nuevas líneas en un...

3  Imitando la mensajería de la célula T9  ( Mimicking t9 cell messaging ) 
Hoy resolví una pregunta de Google Code Jam: imitando un teléfono celular de mensajería. Aquí está el programa, con goto Sé que es un estilo no estructura...

2  Validación instantánea de la longitud del campo de la forma  ( Instantaneous validation of form field length ) 
Dado que tengo una función que se puede hacer de unas maneras como de costumbre, me preguntaba qué sigue es el mejor para lograr la validación del formulario ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos