【问题标题】:Getting generic arguments for SymbolCallerInfo in Roslyn在 Roslyn 中获取 SymbolCallerInfo 的通用参数
【发布时间】:2013-10-17 21:50:45
【问题描述】:

我正在尝试在调用方法 IBus.Publish<T>(来自 NServiceBus)的解决方案中查找所有位置。到目前为止,这是有效的:

IMethodSymbol method = ... [IBus.Publish methodsymbol resolved];
var callers = method.FindCallers(solution, new CancellationToken());

这导致IEnumerable<SymbolCallerInfo>,我得到了对该方法的所有正确引用。

我现在将如何获取通用参数 IBus.Publish 被调用?我是否必须手动解析源树,或者它是否存在一些我可以利用的 Roslyn 魔法?

例子:

在我的代码中:

IBus _bus;

_bus.Publish<IMyMessage>(msg => { msg.Text = "Hello world"});

我有兴趣获得IMyMessage 类型。

非常感谢您的帮助!

【问题讨论】:

  • 为什么不使用反射来做到这一点? :)
  • @animaonline 因为在迭代 n 个解决方案时使用反射来执行此操作,会导致加载不同程序集、影子复制等混乱……我试过了。 :) 此外,Roslyn 是解决此类问题的最佳方式 :)
  • 好的,'FindCallers' 方法是实际方法吗?好像没找到
  • @animaonline 是的,它是 ISymbol 的扩展方法。 IMethodSymbol 又继承 ISymbol
  • 您能解释一下为什么要查找所有调用 Publish 的代码吗?我认为您正在构建的东西对我们来说也可能非常有趣。

标签: c# generics roslyn


【解决方案1】:

您可以使用SemanticModelSyntaxNode 调用到实际的MethodSymbol,然后您只需读取TypeArguments 属性即可获取TypeSymbols 的参数。如果没有明确指定参数,这甚至会起作用,因为SemanticModel 将执行类型推断。

例如:

var callers = method.FindCallers(solution, CancellationToken.None);
foreach (var caller in callers)
{
    foreach (var location in caller.Locations)
    {
        if (location.IsInSource)
        {
            var callerSemanticModel = solution
                .GetDocument(location.SourceTree)
                .GetSemanticModel();
            var node = location.SourceTree.GetRoot()
                .FindToken(location.SourceSpan.Start)
                .Parent;
            var symbolInfo = callerSemanticModel.GetSymbolInfo(node);
            var calledMethod = symbolInfo.Symbol as IMethodSymbol;
            if (calledMethod != null)
            {
                var arguments = calledMethod.TypeArguments;
            }
        }
    }
}

【讨论】:

  • 另外,我认为您可以直接查看 CalledSymbol 的 TypeArguments(在将 CalledSymbol 转换为 IMethodSymbol 之后)。
  • @KevinPilch-Bisson:这似乎给出了未构造的方法,因此每个调用者都会得到Publish&lt;T&gt;,而不是Publish&lt;IMyMessage&gt;Publish&lt;IYourMessage&gt;
  • 这基本上就是我最终做的:)
【解决方案2】:

我对 IMethodSymbol 和 ISymbol 接口不是很熟悉,但这是从 InvocationExpressionSyntax 获取通用参数的另一种方法

var methodRef = (InvocationExpressionSyntax)find_method();

var genericArguments = methodRef.DescendantNodes().OfType<GenericNameSyntax>().FirstOrDefault();

if (genericArguments != null)
   foreach (var g_arg in genericArguments.TypeArgumentList.Arguments)
      Console.WriteLine(g_arg);

static InvocationExpressionSyntax find_method()
{
   var code = new StreamReader("..\\..\\Tests.cs").ReadToEnd();
   SyntaxTree tree = SyntaxTree.ParseText(code);
   var root = tree.GetRoot();
   //find your methods here
   return (InvocationExpressionSyntax)root.DescendantNodes().ToArray()[88];
}

【讨论】:

  • 没错,但这是在解析 SyntaxTree,它并没有真正给我想要的东西。我需要使用 ISymbol 实现,以便找到跨不同解决方案的参考。这会给我一个名为 IMyMessage 的 GenericNameSyntax,但它不会告诉我它属于哪个项目/命名空间。到目前为止,我还无法弄清楚是否可以从 *Syntax 类中获取该信息...
  • 是的,我明白了,但这里有一个可能的“hack”,你可以使用 var parent = genericArguments.Parent; while (!(parent is ClassDeclarationSyntax)) { parent = parent.Parent; } Console.WriteLine(父);
  • @YngveB.Nilsen 如果你需要了解项目和命名空间,那么你不能只使用语法树,你需要语义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多