tags:

views:

175

answers:

5
+10  Q: 

Get the type name

How i can get full right name of generic type?

For example: This code

typeof(List<string>).Name

return

List`1

instead of

List<string>

How to get a right name?

typeof(List<string>).ToString()

returns System.Collections.Generic.List`1[System.String] but i want to get initial name:

List<string>

Is it real?

+2  A: 
typeof(List<string>).ToString()
wRAR
Output: System.Collections.Generic.List`1[System.String]
Hans Passant
+10  A: 

Use the FullName property.

typeof(List<string>).FullName

That will give you the namespace + class + type parameters.

What you are asking for is a C# specific syntax. As far as .NET is concerned, this is proper:

System.Collections.Generic.List`1[System.String]

So to get what you want, you'd have to write a function to build it the way you want it. Perhaps like so:

static string GetCSharpRepresentation( Type t, bool trimArgCount ) {
    if( t.IsGenericType ) {
        var genericArgs = t.GetGenericArguments().ToList();

        return GetCSharpRepresentation( t, trimArgCount, genericArgs );
    }

    return t.Name;
}

static string GetCSharpRepresentation( Type t, bool trimArgCount, List<Type> availableArguments ) {
    if( t.IsGenericType ) {
        string value = t.Name;
        if( trimArgCount && value.IndexOf("`") > -1 ) {
            value = value.Substring( 0, value.IndexOf( "`" ) );
        }

        if( t.DeclaringType != null ) {
            // This is a nested type, build the nesting type first
            value = GetCSharpRepresentation( t.DeclaringType, trimArgCount, availableArguments ) + "+" + value;
        }

        // Build the type arguments (if any)
        string argString = "";
        var thisTypeArgs = t.GetGenericArguments();
        for( int i = 0; i < thisTypeArgs.Length && availableArguments.Count > 0; i++ ) {
            if( i != 0 ) argString += ", ";

            argString += GetCSharpRepresentation( availableArguments[0], trimArgCount );
            availableArguments.RemoveAt( 0 );
        }

        // If there are type arguments, add them with < >
        if( argString.Length > 0 ) {
            value += "<" + argString + ">";
        }

        return value;
    }

    return t.Name;
}

For these types (with true as 2nd param):

typeof( List<string> ) )
typeof( List<Dictionary<int, string>> )

It returns:

List<String>
List<Dictionary<Int32, String>>

In general though, I'd bet you probably don't need to have the C# representation of your code and perhaps if you do, some format better than the C# syntax would be more appropriate.

Adam Sills
Output: System.Collections.Generic.List`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
Hans Passant
Already updated nobugz (his clarifications weren't there originally) :)
Adam Sills
Adam, your code is incorrect. Try it with class C<T> { public class D<U> { } } and GetCSharpRepresentation(typeof(C<int>.D<string>)). You don't get the C# representation.
Eric Lippert
So just a nested type and a nested type where the base type has type arguments? Given an understanding of *why* he'd do this silly thing anyway, I don't doubt there's little legitimate use for this code in the first place, let alone a full functional "get c# representation of a type name" function.
Adam Sills
Yuck. Heh, for completeness' sake I went ahead and updated the sample to handle nested types (both generic and non-generic). It appears to work in all my scenarios.
Adam Sills
typeof( Stupid<int>.Stupider<int> ) gives me Stupid<Int32>+Stupider<Int32> and typeof( Dictionary<int, string>.KeyCollection ) gives me Dictionary<Int32, String>+KeyCollection
Adam Sills
Though using nested types seems to defeat the original desire, which is to match a C# type definition, which this certainly does not do (w/ nested types), since there is no succinct way to represent a nested type in C#.
Adam Sills
+2  A: 

If you have an instance of the list, you can call .ToString() and get the following

System.Collections.Generic.List`1[System.String]

This is in addition to the methods provided by the other answers directly against the type rather than the instance.

Edit: On your edit, I do not believe it is possible without providing your own parsing method, as List<string> is C# shorthand for how the type is implemented, sort of like if you wrote typeof(int).ToString(), what is captured is not "int" but the CTS name, System.Int32.

Anthony Pegram
+2  A: 

You could use this:

public static string GetTypeName(Type t) {
  if (!t.IsGenericType) return t.Name;
  if (t.IsNested && t.DeclaringType.IsGenericType) throw new NotImplementedException();
  string txt = t.Name.Substring(0, t.Name.IndexOf('`')) + "<";
  int cnt = 0;
  foreach (Type arg in t.GetGenericArguments()) {
    if (cnt > 0) txt += ", ";
    txt += GetTypeName(arg);
    cnt++;
  }
  return txt + ">";
}

For example:

static void Main(string[] args) {
  var obj = new Dictionary<string, Dictionary<HashSet<int>, int>>();
  string s = GetTypeName(obj.GetType());
  Console.WriteLine(s);
  Console.ReadLine();
}

Output:

Dictionary<String, Dictionary<HashSet<Int32>, Int32>>
Hans Passant
This is incorrect for the same reason that Adam's code is incorrect; you haven't accounted for how the CLR represents nested generic types.
Eric Lippert
Code corrected :)
Hans Passant
I like your fix :)
Adam Sills
A: 

If you want the base generic type used:

List<string> lstString = new List<string>();
Type type = lstString.GetType().GetGenericTypeDefinition();

Assuming that you want to use the type do do something and that you don't really need the actual string definition which isn't all that useful.

Kelsey