2010-06-10 13 views
6

Quiero analizar un archivo C#. Lo único que quiero es determinar si contiene una propiedad con un nombre específico; solo una respuesta simple verdadero/falso. O mejor dicho, desde que había comprobación de más de una propiedad en cada serie, la extracción de una lista de nombres de propiedades puedan ser de utilidadExtraer nombres de propiedad de un archivo fuente C#

pensé que podía crear una solución elegante usando el CodeDomProvider funcionalidad (f # ejemplo):

use reader = new StreamReader(existingFile) 
let codeProvider = new CSharpCodeProvider() 
let codeUnit = codeProvider.Parse(reader) 

Desafortunadamente, la función Parse no está implementada para el CSharpCodeProvider. ¿Hay alguna manera de obtener un CodeCompileUnit de un archivo fuente? ¿O hay otra manera elegante? (Esperaba evitar expresiones regulares sobre esto)?

Edit: Voy a usar esto para la generación automática de código. Básicamente, voy a generar una clase parcial en el archivo xyz.partial.cs. Esto generará una propiedad de esqueleto. Pero si deseo cambiar la implementación de una propiedad, cortaré esa propiedad y la pegaré en el código xyz.cs. Al volver a crear la clase generada, quiero que salte las propiedades de generación que he movido al archivo codificado a mano.

Por lo tanto, la reflexión está fuera de lugar, porque la reflexión me dirá que la propiedad sí existe, pero no si está definida en uno u otro archivo.

+0

Aún así, estoy ansioso por abrir pop Expresso y darle una oportunidad. Puedo expresar cualquier cosa de forma regular y no un poder en el verso puede detenerme. –

+1

¿Consideró la posibilidad de reflexionar sobre la versión compilada de la fuente? ¿O eso no es una opción? Sin embargo, no estoy seguro de si el reflejo proporciona nombres de propiedad. –

+0

@Moron - Eso debería funcionar bastante bien. – ChaosPandion

Respuesta

0

veces RegEx es la única solución elegante. Esto debería ser lo que estás buscando. Se le dará los nombres de cada propiedad en el archivo de código, y nada más:

(?:"(?:(?:(?:\\.)|[^"\\\r\n])*)"|'(?:(?:(?:\\.)|[^'\\\r\n])*)'|@"(?:(?:(?:"")|[^"])*)")|(?:(?://.*)|(?:/\*(?:(?:[^*]|\*(?!/))*)\*/))|(?:[\w?<>]\s+(\w+)\s*\{\s*(?:get|set)\s*[{;]) 

No va a coincidir con el código similar en los comentarios o cadenas y sólo necesita una pequeña modificación para volver tipo de la propiedad. El nombre aparece en captura \ 1, pero estará en blanco si no es una coincidencia verdadera.

+2

Lo llama "elegante"? "Algunas personas, cuando se enfrentan con un problema, piensan" Lo sé, usaré expresiones regulares. "Ahora tienen dos problemas" –

+0

¿Qué usarías? Bucles y subcadenas anidados Un número fuera de lugar y usted tiene una explosión de problemas impredecible. Regex nunca ha sido un problema para mí, y a menudo hace el trabajo en una sola línea de código. Mucho más eficiente. – Patrick

+0

Me di cuenta de que probablemente necesite usar expresiones regulares, y esta me ayudó a comenzar, pero hay algunas cosas que no coinciden: Propiedades con tipos genéricos o que aceptan nulos (porque el nombre del tipo no termina en un carácter, pero a? o a>), y no coincide con las propiedades automáticas (propiedades con campo de respaldo automático) – Pete

1

EDIT 2: Basado en la información agregada, diría que es mejor compilar la clase codificada a mano y luego reflejar esa clase. A continuación, puede generar el código para el archivo de clase parcial.

EDIT: He investigado un poco más y parece que no has tenido suerte. CodeDom no se puede utilizar para analizar el código. http://blogs.msdn.com/b/bclteam/archive/2005/03/16/396929.aspx

Hay un ejemplo sobre cómo crear una instancia CSharpCodeProvider en MSDN.

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); 
CodeCompileUnit ccu = provider.Parse(reader); 

A continuación, puede navegar por el CodeCompileUnit (más documentación sobre CodeCompileUnit).

Espero que ayude.

+0

Parece que el método Parse no está implementado por todas las clases CodeDomProvider. La pregunta original lo hace sonar como si no hubiera sido implementado por C#. Alguien sabe con certeza? –

+0

Se implementan en tiempo de ejecución, creo. ¿Has intentado realmente ejecutar el código? A menos que arroje excepciones en el tiempo de ejecución, le sugiero que lo intente. –

+0

Así fue como lo comencé. Da el mismo resultado, NotImplementedException. CodeDomProvider.CreateProvider ("CSharp") simplemente devuelve un CSharpCodeProvider. – Pete

0

Creo que puede resolver esto utilizando PropertyInfo en tiempo de ejecución. Utiliza la reflexión para devolver toda la información para un tipo.Para obtener todos los nombres de las propiedades de un tipo, intente esto:

void GetMeTheProperties(object source) 
{ 
    Type sourceType = source.GetType(); 

    foreach (PropertyInfo sourceProperty in sourceType.GetProperties()) 
    { 
     int i = 1; 
     Console.WriteLine("Property {0}: {1}", i, sourceProperty.Name; 
    } 
} 

También puede determinar si una propiedad denominada específica está en un tipo por un método similar:

bool PropertyExists(string propertyName, object source) 
{ 
    Type sourceType = source.GetType(); 
    return (from var s in sourceType.GetProperties() select s).Where(i => i.Name == propertyName).Any(); 
} 
+0

Desafortunadamente, la reflexión no es una opción, vea editar por una razón por la cual – Pete

+0

Lo siento ... malinterpretó la pregunta. – Odhran

0

es el archivo que son generando el código de compilado? De ser así, podría intentar crear un atributo para agregar a todas las propiedades que no deberían copiarse. Luego puede usar la reflexión para leer los atributos y omitir esos.

internal class DoNotCopyAttribute: Attribute{} 

// then add this to Odhran's GetMeTheProperties 
bool skip=false; 
foreach (System.Attribute attr in System.Attribute.GetCustomAttributes(sourceProperty)) { 
    if (attr is DoNotCopyAttribute){ skip=true; break; } 
} 
if(skip) continue; 
0
var provider = CodeDomProvider.CreateProvider("c#"); 
var parameters = new CompilerParameters 
{ 
    WarningLevel = 3 // for example, tune how you need 
}; 
var result = provider.CompileAssemblyFromSource(parameters, new string[] { "source" }); 

if (!result.Errors.HasErrors) 
{ 
    var assembly = result.CompiledAssembly; 

    bool containsLocalAppDomain = assembly 
     .GetTypes() 
     .SelectMany(t => t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) 
     .Any(p => p.Name == "YourProperty"); 

    // indeed it's much better not to load compiled assembly in current appDomain, but create a new one 
    var appDomain = AppDomain.CreateDomain("YourNewDomain", null, null); 
    bool containsNewAppDomain = appDomain 
     .GetAssemblies() 
     .SelectMany(a => a 
      .GetTypes() 
      .SelectMany(t => t.GetProperties(BindingFlags.Instance | BindingFlags.Public))) 
     .Any(p => p.Name == "YourProperty"); 

por cierto, ¿cómo se va a implementar propiedades parciales en cuanto a la no son compatibles?

Cuestiones relacionadas