2010-06-10 3 views
6

Я хочу проанализировать файл C#. Единственное, что я хочу, это определить, содержит ли оно свойство с определенным именем; просто простой истинный/ложный ответ. Вернее, так как я проверка более одного свойства в каждом цикле, извлекая список имен свойств может быть полезнымИзвлечение имен свойств из исходного файла C#

Я думал, что я мог бы создать элегантное решение с использованием CodeDomProvider функциональности (F # пример):

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

К сожалению, функция Parse не применяется для CSharpCodeProvider. Есть ли способ получить CodeCompileUnit из исходного файла? Или есть другой элегантный способ? (Я надеялся избежать регулярных выражений по этому поводу)?

Редактировать: Я собираюсь использовать это для автоматического генерации кода. В принципе, я собираюсь создать частичный класс в файле xyz.partial.cs. Это создаст свойство скелета. Но если я хочу изменить реализацию свойства, я разрежу это свойство и вставим его в кодированный вручную xyz.cs. При воссоздании сгенерированного класса я хочу, чтобы он пропускал генерирующие свойства, которые я переместил в файл с ручной кодировкой.

Следовательно, размышления не могут быть и речи, потому что отражение скажет мне, что свойство действительно существует, но не определено, если оно определено в том или ином файле.

+0

Тем не менее, я испытываю зуд, чтобы вскрыть Expresso и дать ему шанс. Я могу регулярно выражать что-либо, а не сила в стихе, может остановить меня. –

+1

Считаете ли вы размышление над скомпилированной версией источника? Или это не вариант? Не уверен, что отражение отражает имена свойств. – 2010-06-10 13:54:14

+0

@Moron - Это должно работать очень хорошо. – ChaosPandion

ответ

0

Иногда RegEx является единственным элегантным решением. Это должно быть то, что вы ищете. Это даст вам имена каждого свойства в файле кода, и ничего больше:

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

Это не будет соответствовать подобному коду в комментариях или строках и нуждается лишь небольшие изменения, чтобы вернуть тип свойства. Это имя появляется в capture \ 1, но будет пустое, если не истинное совпадение.

+2

Вы называете это «элегантным»? «Некоторые люди, столкнувшись с проблемой, думают« Я знаю, я буду использовать регулярные выражения ». Теперь у них две проблемы» –

+0

Что бы вы использовали? Вложенные петли и подстроки? Один номер неуместен, и у вас есть непредсказуемый взрыв проблем. Regex никогда не был проблемой для меня и часто выполняет работу только в одной строке кода. Гораздо эффективнее. – Patrick

+0

Я понял, что мне, вероятно, нужно использовать регулярное выражение, и это меня запустило, но есть некоторые вещи, которые он не соответствует: свойства с универсальными или нулевыми типами (потому что имя типа не заканчивается символом, но a? или a>), и он не соответствует автоматическим свойствам (свойства с автоматическим полем поддержки) – Pete

1

EDIT 2: Основываясь на дополнительной информации, я бы сказал, что лучше всего компилировать класс с ручным кодированием, а затем отражать этот класс. Затем вы можете сгенерировать код для файла частичного класса.

EDIT: Я провел еще несколько исследований и, похоже, вам не повезло. CodeDom не может использоваться для разбора кода. http://blogs.msdn.com/b/bclteam/archive/2005/03/16/396929.aspx

Существует пример того, как создать экземпляр CSharpCodeProvider на MSDN.

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

Вы можете перемещаться по CodeCompileUnit (больше документации по CodeCompileUnit).

Надеюсь, это поможет.

+0

Похоже, что метод Parse не реализован всеми классами CodeDomProvider. Исходный вопрос звучит так, как будто он не реализован C#. Кто-нибудь знает наверняка? –

+0

Они реализованы во время работы, я думаю. Вы пробовали использовать код? Если это не исключает исключения во время выполнения, я предлагаю вам попробовать. –

+0

Так я и начал. Он дает тот же результат, NotImplementedException. CodeDomProvider.CreateProvider («CSharp») просто возвращает CSharpCodeProvider. – Pete

0

Я думаю, вы можете решить эту проблему, используя PropertyInfo во время выполнения. Он использует отражение для возврата всей информации для типа.Для того, чтобы получить все имена свойств от типа, попробуйте следующее:

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; 
    } 
} 

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

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

К сожалению, отражение не является вариантом, см. Править по той причине, почему – Pete

+0

Извините ... неверно истолковал вопрос. – Odhran

0

ли файл вы генерирование кода из скомпилированного? Если это так, вы можете попробовать создать атрибут для добавления ко всем свойствам, которые нельзя копировать. Затем вы можете использовать отражение для чтения атрибутов и пропустить их.

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"); 

Кстати, как вы собираетесь реализовать частичные свойства, насколько не поддерживаются?

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