c# - Roslyn 是编译时表达式检查的正确工具吗?

标签 c# .net roslyn

我有一个工具包,其中有许多方法经常使用 Expression<Func<T,TProperty>>作为参数。有些只能是单级 ( o=>o.Name ),而有些可以是多级 ( o=>o.EmployeeData.Address.Street )。

我想开发一些东西(MSBuild 任务?Visual Studio 插件?希望是第一个)来读取所有用户的 .cs 文件,如果给定的参数不是属性表达式(而是类似 o=>o.Contains("foo") 的东西),则会出现构建错误), 或者如果在只允许单级的情况下给出了多级表达式。

我尝试先查看已编译的 IL 代码,但由于表达式树是 C# 编译器的“技巧”,在 IL 中,我所看到的只是创建表达式实例等,而我可以检查每个 if仅创建了 MemberExpressions(以及它们的正确数量),它不是很好。

然后我想到了 Roslyn。 是否可以用 Roslyn 编写类似这样的东西?

最佳答案

是的,我认为 Roslyn 及其代码问题正是解决此问题的正确工具。使用它们,您可以在键入和创建在 Visual Studio 中显示为其他错误的错误(或警告)时分析代码。

我曾尝试创建这样的代码问题:

[ExportSyntaxNodeCodeIssueProvider("PropertyExpressionCodeIssue", LanguageNames.CSharp, typeof(InvocationExpressionSyntax))]
class PropertyExpressionCodeIssueProvider : ICodeIssueProvider
{
    [ImportingConstructor]
    public PropertyExpressionCodeIssueProvider()
    {}

    public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxNode node, CancellationToken cancellationToken)
    {
        var invocation = (InvocationExpressionSyntax)node;

        var semanticModel = document.GetSemanticModel(cancellationToken);

        var semanticInfo = semanticModel.GetSemanticInfo(invocation, cancellationToken);

        var methodSymbol = (MethodSymbol)semanticInfo.Symbol;

        if (methodSymbol == null)
            yield break;

        var attributes = methodSymbol.GetAttributes();

        if (!attributes.Any(a => a.AttributeClass.Name == "PropertyExpressionAttribute"))
            yield break;

        var arguments = invocation.ArgumentList.Arguments;
        foreach (var argument in arguments)
        {
            var lambdaExpression = argument.Expression as SimpleLambdaExpressionSyntax;
            if (lambdaExpression == null)
                continue;

            var parameter = lambdaExpression.Parameter;
            var memberAccess = lambdaExpression.Body as MemberAccessExpressionSyntax;
            if (memberAccess != null)
            {
                var objectIdentifierSyntax = memberAccess.Expression as IdentifierNameSyntax;

                if (objectIdentifierSyntax != null
                    && objectIdentifierSyntax.PlainName == parameter.Identifier.ValueText
                    && semanticModel.GetSemanticInfo(memberAccess, cancellationToken).Symbol is PropertySymbol)
                    continue;
            }

            yield return
                new CodeIssue(
                    CodeIssue.Severity.Error, argument.Span,
                    string.Format("Has to be simple property access of '{0}'", parameter.Identifier.ValueText));
        }
    }

    #region Unimplemented ICodeIssueProvider members

    public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxToken token, CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }

    public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxTrivia trivia, CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }

    #endregion
}

用法是这样的:

[AttributeUsage(AttributeTargets.Method)]
class PropertyExpressionAttribute : Attribute
{ }

…

[PropertyExpression]
static void Foo<T>(Expression<Func<SomeType, T>> expr)
{ }

…

Foo(x => x.P);   // OK
Foo(x => x.M()); // error
Foo(x => 42);    // error

上面的代码有几个问题:

  1. 它完全没有优化。
  2. 它可能需要更多的错误检查。
  3. 它不起作用。至少在当前的 CTP 中是这样。靠近末尾的表达式 semanticModel.GetSemanticInfo(memberAccess, cancellationToken).Symbol 始终返回 null。这是因为表达式树的语义在the currently unimplemented features之间。 .

关于c# - Roslyn 是编译时表达式检查的正确工具吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9768233/

相关文章:

c# - 比较 C# 中的两个 List<enum> 对象

c# - MySql 数据库中的 Entity Framework 代码优先迁移

.NET 框架版本

c# - 如何使用包含的源代码生成器发布 .Net 库?

c# - Unity 无法解析通用存储库

c# - 使用 entityframework 重用 Linq to SQL 代码

.net - 使用字典时 .NET 4 并发性能差

c# - 将 cookie 从 CookieContainer 写入 IE cookie 存储

c# - VSIX-项目 : Unable to get current Workspace due to casting issues

c# - 通过 Roslyn 编译时找不到“主要”方法