views:

329

answers:

4

I'm trying to write a function to generate the full C# declaration for a Type object. My current method involves performing very manual and specific logic on the Type object.

Is there some built in way to .Net to generate this declaration?

As an example, take this class:

namespace My.Code.Here
{
   public class Class1<>
   {
      public enum Enum1 { }
   }
}

when the function (lets call it getCSharpDec) is called on typeof(Class1<>.Enum1), it would return "My.Code.Here.Class1<>.Enum1".

+3  A: 

There are a couple of problems here...

  • C# and Type name nested types differently
  • C# and Type name generic types differently

As a minor aside, Class1<>.Enum1 isn't a closed type, but that shouldn't be an issue...

(edit)

This gets pretty close - it still retains the outer generics from the type, though:

static void Main()
{
    Type t = typeof(My.Code.Here.Class1<>.Enum1);
    string s = GetCSharpName(t); // My.Code.Here.Class1<T>.Enum1<T>
}

public static string GetCSharpName<T>()
{
    return GetCSharpName(typeof(T));
}
public static string GetCSharpName(Type type)
{
    StringBuilder sb = new StringBuilder();
    sb.Insert(0, GetCSharpTypeName(type));
    while (type.IsNested)
    {
        type = type.DeclaringType;
        sb.Insert(0, GetCSharpTypeName(type) + ".");

    }
    if(!string.IsNullOrEmpty(type.Namespace)) {
        sb.Insert(0, type.Namespace + ".");
    }
    return sb.ToString();
}
private static string GetCSharpTypeName(Type type)
{

    if (type.IsGenericTypeDefinition || type.IsGenericType)
    {
        StringBuilder sb = new StringBuilder();
        int cut = type.Name.IndexOf('`');
        sb.Append(cut > 0 ? type.Name.Substring(0, cut) : type.Name);

        Type[] genArgs = type.GetGenericArguments();
        if (genArgs.Length > 0)
        {
            sb.Append('<');
            for (int i = 0; i < genArgs.Length; i++)
            {
                sb.Append(GetCSharpTypeName(genArgs[i]));
                if (i > 0) sb.Append(',');
            }
            sb.Append('>');
        }
        return sb.ToString();
    }
    else
    {
        return type.Name;
    }
}
Marc Gravell
Nice example with lot's of the potential special cases. As a little note the for loop on genArgs can be substituted with a string.Join(",",genArgs.Select(x => x.FullName). If there's a difference in speed my guess is that Marcs code might be faster but I'd personally preferre the one liner to the loop for readability
Rune FS
and to make them functionally equivalent the select should of cause use GetCSharpTypeName(T) and not .FullName
Rune FS
Thanks for the code Marc, your code looks very similar to my current code. I was hoping to find a solution that didn't involve the specific intricacies (or atleast wrap then by an external function :)
NoizWaves
Don't forget that `Type` needs to support all languages... and they always share much syntax... so there is little CLR -> C# translation.
Marc Gravell
+1  A: 

Do you mean you want something like the typeof(Class1<>.Enum1).FullName?

Note though as Marc indicates, the name this gives you might not be exactly what you want if you need exactly the format you specified.

jerryjvl
+1  A: 

Type.FullName is what you're looking for.

Samuel Jack
No, that will be a very different string - something like: My.Code.Here.Class1`1+Enum1
Marc Gravell
A: 

This code should work for nested generic types (e.g. Foo<int>.Bar<string,object>).

public static string GetCSharpTypeName(this Type type, bool getFullName)
{
    StringBuilder sb = new StringBuilder();
    if (getFullName && !string.IsNullOrEmpty(type.Namespace))
    {
        sb.Append(type.Namespace);
        sb.Append(".");
    }
    AppendCSharpTypeName(sb, type, getFullName);
    return sb.ToString();
}

private static void AppendCSharpTypeName
    (StringBuilder sb, Type type, bool fullParameterNames)
{
    string typeName = type.Name;
    Type declaringType = type.DeclaringType;

    int declaringTypeArgumentCount = 0;
    if (type.IsNested)
    {
        if (declaringType.IsGenericTypeDefinition)
        {
            declaringTypeArgumentCount = 
                declaringType.GetGenericArguments().Length;
            declaringType = declaringType.MakeGenericType(
                type.GetGenericArguments().Take(declaringTypeArgumentCount)
                    .ToArray());
        }

        AppendCSharpTypeName(sb, declaringType, fullParameterNames);
        sb.Append(".");
    }
    Type[] genericArguments = type.GetGenericArguments()
        .Skip(declaringTypeArgumentCount).ToArray();

    int stopIndex;
    if ((type.IsGenericTypeDefinition || type.IsGenericType)
        && ((stopIndex = type.Name.IndexOf('`')) > 0))
    {
        sb.Append(typeName.Substring(0, stopIndex));
        string[] genericArgumentNames = genericArguments
            .Select(t => GetCSharpTypeName(t, fullParameterNames)).ToArray();
        if (genericArgumentNames.Length > 0)
            sb.AppendFormat("<{0}>", string.Join(",", genericArgumentNames));
    }
    else
    {
        sb.Append(typeName);
    }
}