解析这个字符串的更优雅的方法是什么?

Dan*_*mta 1 c# parsing

我有一个任务,我需要解析C#脚本并寻找某个方法属性并从中提取部分,我想知道是否有一种比我更优雅的方式:

[Info("Title", "Author", "5.2.5", ResourceId = 819)]

这是我做的:

// foreach line in script
if (line.Contains("[Info(") && line.Contains("ResourceId"))
{
    var _attributes = line
        .Replace(" ", "")
        .Replace("\"", "")
        .Replace("[Info(", "")
        .Replace(")]", "")
        .Replace("ResourceId=", "")
        .Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
        // Do stuff with _attributes[0] _attributes[1] etc..
        break;
}
Run Code Online (Sandbox Code Playgroud)

Lua*_*aan 5

现在最简单的解决方案是使用Roslyn.您可以解析代码,查找实际属性(而不是看起来像您正在寻找的属性的东西),并以C#-proper的方式处理它们.

这是一个简单的例子:

var infoAttributes = CSharpSyntaxTree.ParseText(@"
namespace MyNamespace
{
    public class SomeClass
    {
        const string SomeConstant = ""Hi!"";

        [Info(""Some book"", ""Ray Brandenburg"", ""5.2.5"", ResourceId = 819)]
        public void SomeMethod()
        {

        }

        [InfoAttribute(SomeConstant, 42, ""Banana"")]
        public void SomeMethod2()
        {

        }

        // [Info(""Not going to happen"", ""Hilary Clinton"", ""1.2.0"")]
        public void SomeMethod3()
        {

        }
    }
}
")
.GetRoot()
.DescendantNodes()
.OfType<AttributeSyntax>()
.Where(i => i.Name.ToString() == "Info" || i.Name.ToString() == "InfoAttribute")
.Where
(
  i => 
    i.ArgumentList.Arguments.Count(j => j.NameEquals == null) == 3 
    && i.ArgumentList.Arguments[0].GetFirstToken().IsKind(SyntaxKind.StringLiteralToken)
    && i.ArgumentList.Arguments[1].GetFirstToken().IsKind(SyntaxKind.StringLiteralToken)
    && i.ArgumentList.Arguments[2].GetFirstToken().IsKind(SyntaxKind.StringLiteralToken)
)
.Select
(
  i =>
  new 
  {
    Title = (string)i.ArgumentList.Arguments[0].GetFirstToken().Value,
    Author = (string)i.ArgumentList.Arguments[1].GetFirstToken().Value,
    Version = (string)i.ArgumentList.Arguments[2].GetFirstToken().Value,
    ResourceId = 
      i.ArgumentList.Arguments
       .Where(j => j.NameEquals != null && j.NameEquals.Name.ToString() == "ResourceId")
       .Select(j => j.ChildNodes().Skip(1).First().GetFirstToken().Value.ToString())
       .FirstOrDefault()
  }
);

infoAttributes.Dump();
Run Code Online (Sandbox Code Playgroud)

在这个级别,这只是解析源代码.为了使事情变得更简单,我添加了防御性条款,只使用字面值来实现这一点 - 你可能希望将它们变成警告,以便手动处理或者其他东西.代码正确处理任何琐事(例如空白),代码看起来像属性声明但不是,评论和许多其他可能的问题.还有一个简化的假设 - 值必须是文字(字符串或其他).该示例将只找到一个Info属性 - 一个SomeMethod2使用常量和不同的构造函数重载,并且一个on SomeMethod3被注释掉.

另一个层次是从中创建一个编译树.这涉及更多,但允许您使一切工作,就像它是真正的C#代码 - 例如,属性SomeMethod2SomeConstant正确解析.当然,如果你真的想要100%正确,这需要收集所有的依赖等,这听起来像是一种矫枉过正.除非这是您的代码中的真正问题,否则警告应该对异常值有效.如果在代码中经常使用局部常量,那么扩展代码以处理局部文字常量仍然非常容易.

作为免责声明,这肯定不是使用Roslyn进行解析的最佳方式.这只是我想到的第一件事,花了一段时间才开始.我几乎每天都在寻找更好的方式来处理Roslyn :)