20

Is it somehow possible to get the fully qualified name of the type contained in a TypeInfo object?

In the debugger many of these values nicely show up as System.Int32 but when it's printed out, not one of them contains this fully qualified name. I need this to provide as an argument to Type.GetType().

var typeInfo = semanticModel.GetTypeInfo(argument);
var w = typeInfo.ToString(); // Microsoft.CodeAnalysis.TypeInfo
var y = typeInfo.Type.ToString(); // int
var z = typeInfo.Type.ToDisplayString(); // int 
var a = typeInfo.Type.OriginalDefinition.ToDisplayString(); // int
var b = typeInfo.Type.OriginalDefinition.ToString(); // int
var c = typeInfo.Type.Name; // Int32
var d = typeInfo.Type.MetadataName; // Int32
var e = typeInfo.Type.ToDisplayParts(); // {int}
var f = typeInfo.Type.ContainingNamespace; // System

Note that this should work for every type so I can't just concatenate the namespace with the name.

Alternatively: is there another (more suited?) way to get the exact type?

For context: I want to check if the type-parameters of a class contain a few specific methods. Therefore my approach was to get the parameters from the TypeArgumentListSyntax and get the TypeInfo from each TypeSyntax object.

Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170

3 Answers3

35

The ToDisplayString method lets you pass in a "format" object which has a huge number of options for controlling how you want to format stuff:

var symbolDisplayFormat = new SymbolDisplayFormat(
    typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);

string fullyQualifiedName = typeSymbol.ToDisplayString(symbolDisplayFormat);

The reason your getting keywords like "int" is the default format is including the SymbolDisplayMiscellaneousOptions.UseSpecialTypes flag which specifies to use the language keywords for special types vs. the regular name.

Jason Malinowski
  • 18,148
  • 1
  • 38
  • 55
  • This works indeed, thanks! I have a side-issue though: `Type.GetType(Tuple)` does not give me the `Type` of `Tuple` and these are not the same in my situation. As far as I can tell, I can't pass any type arguments to `Type.GetType(string)` (aka: `Type.GetType("Tuple")`), what would be my best option here to get the correct type? – Jeroen Vannevel Apr 26 '14 at 18:58
  • You have to pass the type arguments in CLR form, with square brackets and a few other things. Honestly, this is one place Roslyn doesn't quite have the APIs you need to make this easy. :-( – Jason Malinowski Apr 26 '14 at 19:41
  • Yeah, it would have made a big difference in my code's readability if a `TypeInfo` object would have a reference to the exact `Type` (which I would expect it to do). Using the tickform notation it works as it should, but only for built-in types. Even when I add the containing assembly to the string, it can't locate the class I defined myself. Is there something else I should do to be able to get a type defined by the user? – Jeroen Vannevel Apr 26 '14 at 20:22
  • Nice, I didn't know about this! – andyp Apr 26 '14 at 22:24
  • 3
    Well, so getting a Type for TypeInfo, naming issues aside, is a tricky problem. It assumes you have an assembly that can be loaded and found. When you do a build, the compiler might be loading reference assemblies that themselves can't be loaded as "normal" assemblies. Even if they are, you might have to hook AppDomain.AssemblyResolve to locate your references, and whatever assembly you built. – Jason Malinowski Apr 27 '14 at 00:05
  • 1
    "Build" and "runtime" are really different domains, and crossing from one to the other is poorly defined, at best. I assume here that you _really need_ a System.Type because you're using some other reflection API, or trying to then load that type and execute code from it. – Jason Malinowski Apr 27 '14 at 00:08
  • @JasonMalinowski: I have made a followup post on this problem [here](http://stackoverflow.com/questions/23324735/getting-the-system-type-from-an-assembly-loaded-at-runtime). – Jeroen Vannevel Apr 27 '14 at 14:40
5

I couldn't find something built-in either and I'm quite sure this isn't the most elegant way, but it worked for me to construct a qualified type name like this:

private static string GetQualifiedTypeName(ISymbol symbol)
{
    return symbol.ContainingNamespace 
        + "." + symbol.Name 
        + ", " + symbol.ContainingAssembly;
}

If you don't need an assembly qualified type name don't concatenate ContainingAssembly at the end of the last line.

andyp
  • 6,229
  • 3
  • 38
  • 55
  • Okay so apparantly I was misinterpreting about the namespace and in fact this *looks* like it should work: for example it returns me `RoslynTester.Collections.MyTestClass, RoslynTester, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null`. However when I use this in conjunction with `Type.GetType()` thena `null` value is returned. Why wouldn't it be able to find this? – Jeroen Vannevel Apr 26 '14 at 18:04
  • Hi, the assembly containing your custom type has to be loaded into your program doing the parsing for Type.GetType(..) to able to 'know' your type at runtime. – andyp Apr 26 '14 at 22:24
  • Mhhm good point, although `Assembly.Load(symbol.ContainingAssembly)` only throws me an exception saying it couldn't "load the file or assembly or one of its dependencies. The system cannot find the file specified." Surely I would expect it to find an assembly which I got the string representation from through Roslyn itself. – Jeroen Vannevel Apr 26 '14 at 23:41
  • This worked for me, except I had to use `MetadataName` as opposed to `Name` – joshcomley Feb 22 '18 at 12:36
0

Using the semantic model you can also do it like i did it here:

var typeInfo = context.SemanticModel.GetTypeInfo(identifierNameSyntax);
var namedType = typeInfo.Type as INamedTypeSymbol;
if (namedType != null && namedType.Name == nameof(ConfiguredTaskAwaitable) && GetFullNamespace(namedType) == typeof(ConfiguredTaskAwaitable).Namespace)
    return true;

where "GetFullNamespace" works like this:

    public static IEnumerable<string> GetNamespaces(INamedTypeSymbol symbol)
    {
        var current = symbol.ContainingNamespace;
        while (current != null)
        {
            if (current.IsGlobalNamespace)
                break;
            yield return current.Name;
            current = current.ContainingNamespace;
        }
    }

    public static string GetFullNamespace(INamedTypeSymbol symbol)
    {
        return string.Join(".", GetNamespaces(symbol).Reverse());
    }

    public static string GetFullTypeName(INamedTypeSymbol symbol)
    {
        return string.Join(".", GetNamespaces(symbol).Reverse().Concat(new []{ symbol.Name }));
    }

Obviously Jason Malinowski's answer is more convenient for simple cases

Dbl
  • 5,634
  • 3
  • 41
  • 66