从C#源文件中提取属性名称

7
我想解析一个c#文件,唯一需要的是确定它是否包含具有特定名称的属性;只需简单的true/false响应。或者更确切地说,由于我在每次运行时都要检查多个属性,提取属性名称列表可能会很有帮助。
我认为可以使用CodeDomProvider功能(f#示例)创建一个优雅的解决方案:
use reader = new StreamReader(existingFile)
let codeProvider = new CSharpCodeProvider()
let codeUnit = codeProvider.Parse(reader)

很遗憾,CSharpCodeProvider 没有实现 Parse 函数。是否有一种方法可以从源文件获取 CodeCompileUnit?或者有其他更优雅的方法吗?(我希望避免在此使用正则表达式)。
编辑: 我将用它来进行自动代码生成。基本上,我将在文件 xyz.partial.cs 中生成一个部分类。这将生成一个框架属性。但是,如果我想要更改属性的实现,我将剪切该属性并将其粘贴到手工编写的 xyz.cs 文件中。重新创建生成的类时,我希望它跳过生成我已移动到手工编写文件中的属性。 因此,反射不可行,因为反射会告诉我该属性确实存在,但不会告诉我它是在哪个文件中定义的。

不过,我很想打开Expresso并尝试一下。我可以使用正则表达式处理任何东西,没有任何力量可以阻止我。 - Swingline Rage
1
你有考虑过在源代码的编译版本上使用反射吗?或者这不是一个选项?不确定反射是否提供属性名称。 - Aryabhatta
@Moron - 这实际上应该很有效。 - ChaosPandion
@Moron - 反射不是一个选项,请查看编辑的原因。 - Pete
6个回答

0
有时候正则表达式是唯一优雅的解决方案。这应该是你正在寻找的。它将为您提供代码文件中每个属性的名称,而不会多余地返回其他内容:
(?:"(?:(?:(?:\\.)|[^"\\\r\n])*)"|'(?:(?:(?:\\.)|[^'\\\r\n])*)'|@"(?:(?:(?:"")|[^"])*)")|(?:(?://.*)|(?:/\*(?:(?:[^*]|\*(?!/))*)\*/))|(?:[\w?<>]\s+(\w+)\s*\{\s*(?:get|set)\s*[{;])

它不会匹配注释或字符串中的类似代码,并且只需要进行小修改即可返回属性的类型。名称出现在捕获\1中,但如果不是真正的匹配,则为空。


2
你把它称作“优雅”吗? “有些人遇到问题时,会想‘我知道了,我要用正则表达式。’现在他们有两个问题了。” - remi bourgarel
你会用什么?嵌套循环和子字符串吗?只要有一个数字错位,就会导致无法预测的问题爆发。对我来说,正则表达式从来不是问题,通常只需要一行代码就能完成工作。更加高效。 - Patrick
我意识到我可能需要使用正则表达式,这个给了我一个好的起点,但它还有一些不匹配的东西:具有泛型或可空类型的属性(因为类型名称不以字符结束,而是以?或>),它也不匹配自动属性(带有自动支持字段的属性)。 - Pete
现在已经可以,我做了一些测试以确保它可以运行。如果你想分开它,你可以编写一个删除所有不需要的代码,直到只剩下更容易管理的内容的程序。我同意大型代码块(超过2行且不包含静态字符串)对于没有准备好的人来说可能是一场噩梦,但是嘿,这就是注释存在的目的。 - Patrick

0

编辑2: 根据添加的信息,我认为最好编译手工编写的类,然后反射该类。然后,您可以生成部分类文件的代码。

编辑: 我进行了更多研究,发现您没有办法。 CodeDom 不能用于解析代码。http://blogs.msdn.com/b/bclteam/archive/2005/03/16/396929.aspx

有一个关于如何在MSDN上创建 CSharpCodeProvider 实例的示例。

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

然后您可以浏览CodeCompileUnit(有关CodeCompileUnit的更多文档)。

希望能帮到您。


看起来并不是所有的CodeDomProvider类都实现了Parse方法。原始问题让人觉得C#没有实现它。有人确定吗? - Don Kirkby
我认为它们是在运行时实现的。你试过真正运行代码吗?除非它在运行时抛出异常,否则建议你尝试一下。 - Jonas Van der Aa
那就是我开始的方式。它会给出相同的结果,即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();
}

很遗憾,反射不是一个选项,请参见编辑以了解原因。 - Pete
抱歉...误解了问题。 - 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");

顺便问一下,既然不支持,你打算如何实现部分属性?


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接