SztupY
SztupY

Reputation: 10536

Convert generic type definition strings from C# style to CLR style

I want to convert a C# style generic type string, like:

"System.Dictionary<System.String, System.String>"

To its CLR equivalent:

"System.Dictionary`1[System.String, System.String]"

and back. Is there an easy way to do this, or do I have to resort to string manipulation?

EDIT:

I only have the string representation in C#/VB/etc style form. I don't have a Type object or something similar at hand. An alternate question would have been: how to get a Type object from a string representation like "System.Dictionary<System.String, System.String>" (after that I can get the full name of the type object).

EDIT#2:

Justification: I'm creating objects dynamically using CodeDom. The input from which I'm generating the CodeDom might contain types in C# (or any other proprietary like VB.NET) format, but I have to use them inside a CodeTypeReference, which only accepts the CLR style format.

Upvotes: 3

Views: 2141

Answers (6)

paulparayil
paulparayil

Reputation: 36

I believe I faced a similar issue in the past and wrote the following utility method to address it :

    // Utilitiy to convert class name string to namespace-qualified name
    public static Type GetTypeByName(string ClassName, 
        string TypesNamespacePrefix = "AssemblyTopLevel.AssemblyMidLevel.", 
        Dictionary<string, Type> ConcreteTypeMap = null)
    {
        if ((ConcreteTypeMap != null) && (ConcreteTypeMap.ContainsKey(ClassName)))
            return ConcreteTypeMap[ClassName];

        if ((ConcreteTypeMap != null) && (ConcreteTypeMap.ContainsKey(TypesNamespacePrefix + ClassName)))
            return ConcreteTypeMap[TypesNamespacePrefix + ClassName];

        try
        {
            if (Type.GetType(ClassName) != null)
                return Type.GetType(ClassName);
        }
        catch { }

        try
        {
            if (Type.GetType(TypesNamespacePrefix + ClassName) != null)
                return Type.GetType(TypesNamespacePrefix + ClassName);
        }
        catch { }

        Stack<int> GenericCounterStack = new Stack<int>();
        Stack<int> GenericStartIndexStack = new Stack<int>();
        Dictionary<int, int> GenericCountMapByStartIndex = new Dictionary<int, int>();
        int Count = 1;
        int GenericStartIndex = -1;
        int PreviousHighestGenericIndex = -1;

        foreach (char c in ClassName)
        {
            if (c.Equals('<'))
            {
                if (GenericStartIndex != -1)
                {
                    GenericCounterStack.Push(Count);
                    GenericStartIndexStack.Push(GenericStartIndex);
                }
                Count = 1;
                GenericStartIndex = PreviousHighestGenericIndex + 1;
                PreviousHighestGenericIndex = Math.Max(GenericStartIndex, PreviousHighestGenericIndex);
            }
            else if (c.Equals(','))
            {
                Count++;
            }
            else if (c.Equals('>'))
            {
                GenericCountMapByStartIndex[GenericStartIndex] = Count;
                if (GenericCounterStack.Count != 0)
                    Count = GenericCounterStack.Pop();
                if (GenericStartIndexStack.Count != 0)
                    GenericStartIndex = GenericStartIndexStack.Pop();
            }
        }

        ClassName = ClassName.Replace("<" + TypesNamespacePrefix, "<");

        StringBuilder FullyQualifiedClassName = new StringBuilder(TypesNamespacePrefix);

        GenericStartIndex = 0;
        foreach (char c in ClassName)
        {
            if (c.Equals('<'))
            {
                FullyQualifiedClassName.Append("`" + GenericCountMapByStartIndex[GenericStartIndex].ToString()
                    + "[" + TypesNamespacePrefix);
                GenericStartIndex++;
            }
            else if (c.Equals(','))
            {
                FullyQualifiedClassName.Append("," + TypesNamespacePrefix);
            }
            else if (c.Equals('>'))
            {
                FullyQualifiedClassName.Append("]");
            }
            else
                FullyQualifiedClassName.Append(c);
        }

        ClassName = FullyQualifiedClassName.ToString();

        return Type.GetType(ClassName);
    }

You may optionally provide a concrete type map, to optimize for commonly-invoked cases:

static readonly Dictionary <string, Type> MyConcreteTypeMap = new Dictionary<string,Type>()
    {
        {"SomeClass", typeof(SomeClass)},
        {"AnotherClass", typeof(AnotherClass)}
    }

So, the invocation looks like this:

Type DesiredType = GetTypeByName("SomeClass", "", MyConcreteTypeMap);

or

Type DesiredType = GetTypeByName("SomeOtherClass", "MyAssemblyTopLevel.MyAssemblyMidLevel.");

or

Type DesiredType = GetTypeByName("SomeOtherClass");

or, quite usefully,

Type DesiredType = GetTypeByName("SomeOtherClass<OuterTemplate<InnerTemplate1,InnerTemplate2<InnerInnerTemplate1>>>");

or, for the original example,

Type DesiredType = GetTypeByName("Dictionary<String,String>", "System.");

Upvotes: 2

kvb
kvb

Reputation: 55184

For the .NET to C# direction, I can't see an easy way to do it; you may have to actually parse the type yourself.

In the other direction, it's pretty easy:

public static string DotNetToCSharp(string tyName) {
    var provider = new Microsoft.CSharp.CSharpCodeProvider();
    return provider.GetTypeOutput(new System.CodeDom.CodeTypeReference(tyName));
}

Upvotes: 4

Igor Zevaka
Igor Zevaka

Reputation: 76500

typeof(System.Dictionary<System.String, System.String>).FullName should get you something like:

System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

Might need to do some replacement on the full type names inside the type parameter spec. However if you use the below code

typeof(Dictionary<System.String, System.String>).ToString()

You will get something like this:

System.Collections.Generic.Dictionary`2[System.String,System.String]

To convert back into a Type use

Type.GetType("System.Collections.Generic.Dictionary`2[System.String,System.String]");

EDIT I've tried hacking this with dynamic expressions but didn't have much luck. I think, other than parsing the string, you can use a "nuclear option" and compile a DLL using CSharpCodeProvider and then load it using Assembly.LoadFile and execute a method that will evaluate typeof(...).

Upvotes: 0

Matthew Whited
Matthew Whited

Reputation: 22443

I think this is what you were looking for...

var dict = new Dictionary<string, string>();
var type = dict.GetType();
var typeName = type.FullName;
var newType = Type.GetType(typeName);

Console.WriteLine(type == newType); //true

Upvotes: 0

Itay Karo
Itay Karo

Reputation: 18286

I am not sure I completely understand your question. Anyway -when you instantiate a genetic class Class<T1, ... ,Tn> the defined in namespace Namespace, for example

Namespace.Class<T1, ... ,Tn> a = new Namespace.Class<Type1,...,Typen>();

a.GetType().FullName will give you the full string representation of the class which will be "Namespace.Class'n<Type1,...,Typen>"

Upvotes: 0

Aren
Aren

Reputation: 55946

typeof(System.Dictionary<System.String>, System.String>).FullName

As for the reverse, no clue :(

Upvotes: 0

Related Questions