11

私は、多くの場合パラメータとして使用する多くのメソッドを備えたツールキットを持ってExpression<Func<T,TProperty>>います。一部はシングルレベルのみ(o=>o.Name)にすることができ、一部はマルチレベル(o=>o.EmployeeData.Address.Street)にすることができます。

o=>o.Contains("foo")すべてのユーザーの.csファイルを読み取り、指定されたパラメーターがプロパティ式ではない場合(ただし、のようなもの)、またはマルチの場合にビルドエラーを発生させるもの(MSBuildタスク?Visual Studioプラグイン?できれば最初のもの)を開発したい-レベル式は、単一レベルのみが許可される場合に指定されます。

最初にコンパイルされたILコードを調べてみましたが、式ツリーはC#コンパイラの「トリック」であるため、ILでは式インスタンスなどを作成するだけで、MemberExpressions(およびそれらの正しい数)のみを確認できます。作成されます、それはそれほど素晴らしいではありません。

それからRoslynが頭に浮かびました。Roslynでこのようなものを書くことは可能ですか?

4

2 に答える 2

10

はい、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。これは、式ツリーのセマンティクスが現在実装されていない機能の1つであるためです。
于 2012-03-19T22:47:35.007 に答える
5

はい、それは完全に可能です。問題は、Roslynがまだすべての言語構造をサポートしていないため、サポートされていないものに遭遇する可能性があることです。式ツリーは、Roslynが式を生成するコードをコンパイルできないという点でサポートされていませんが、いくつかのことを機能させるのに十分な距離をとることができるはずです。

大まかに言えば、これをMSBuildタスクとして実装する場合は、ビルドタスクでRoslyn.Services.Workspace.LoadSolutionまたはを呼び出すことができますRoslyn.Services.Workspace.LoadStandaloneProject。次に、構文ツリーを調べてさまざまなメソッドの言及を探し、それらをバインドして、実際に呼び出していると思われるメソッドであることを確認します。そこから、ラムダ構文ノードを見つけて、そこから必要な構文/セマンティック分析を実行できます。

RFxCopConsoleCSCTPには、Roslynで単純なFxCopスタイルのルールを実装するプロジェクトなど、役立つと思われるサンプルプロジェクトがいくつかあります。

また、Roslynのパーサーは完全であるため、セマンティック情報なしで実行できることが多いほど、優れていることにも言及する必要があります。

于 2012-03-19T21:58:42.983 に答える