unknown6656
unknown6656

Reputation: 2963

Create any type instance from its string via reflection

Imagine the following scenario:

A user passes a string to the application, which represents a .NET-Type, e.g. string or System.IntPtr. Assuming, that the application in question has access to the assemblies, in which the given Types are defined, it can create an instance of the given type based on the string.

I have managed to create such an application; the problem is only, that I am not (yet) able to create "complex" or "nested" types, e.g.:

My question is: How can I create such nested type instances, when only given the string representation of the type (and of course all required assemblies)?

Do the .NET BCL contain some sort of string --> Type-parser? Or do I have to use some form of recursive regex-parser and Activator.CreateInstance?


EDIT №1:
As this question seems to be too broad, I will try to clarify myself:

I am able to create type instances for 'simple' types - I am, however, struggling to create type instances of 'complex' types, e.g.:

Type t1 = typeof(string).Assembly.GetType("System.String");
Type t2 = typeof(string).Assembly.GetType("System.Tuple<System.String, System.Int32>");
object obj1 = Activator.CreateInstance(t1);
object obj2 = Activator.CreateInstance(t2);

obj1 is an instance of string, but obj2 will fail, as the variable t2 is null (Meaning, that the second line in the given code was unable to find the 'complex' type).


EDIT №2:
The usage of Type::MakeGenericType(Type[]) is obviously helpful, but how would be the easiest/fastest(?) way to create an instance of a type like this:
IEnumerable<Tuple<int[,], char[][], Dictionary<string, Func<int, int*, byte>>>>
I would have to write some kind of regex-based parser, which iterates recursively over the different sections of the given string....


EDIT №3:
I have discovered System::CodeDom::CodeTypeReference and I am currently searching for a method to get the type based on a given CodeTypeReference-instance (which can be created using the type's string representation).


EDIT №4: (sorry for all the edits)
I think I found my solution in this SO post.

Upvotes: 0

Views: 723

Answers (2)

unknown6656
unknown6656

Reputation: 2963

I solved it (temporarily) as follows:

  1. Check if the given string contains < or >
  2. If so, parse the string according to the function given below (ParseGeneric) and continue recursively using the parsed tokens
  3. If not, check for any occurrence of array brackets ([], [][], [,] etc.)
  4. Parse the type accordingly using a recursive Array::CreateInstance(Type,int[])
  5. Check for any occurrence of pointer 'stars' (*) and parse the type accordingly using Marshal::StructureToPtr


My ParseGeneric-method:

public static Type FetchGenericType(string typestring)
{
    Match m;

    if ((m = Regex.Match(typestring, )).Success)
    {
        string cls = m.Groups["class"].ToString();
        string par = m.Groups["params"].Success ? m.Groups["params"].ToString() : "";

        List<string> paramtypes = new List<string>();
        int s = 0, e = 0;

        for (int i = 0, c = 0, l = par.Length; i < l; i++)
            switch (par[i])
            {
                case ',':
                    if (c > 0)
                        goto default;

                    paramtypes.Add(par.Substring(s, e));

                    s = i + 1;
                    e = 0;
                    break;
                case '<': ++c;
                    goto default;
                case '>': --c;
                    goto default;
                default: ++e;
                    break;
            } // I know, that this is bad as hell, but what should I do instead?

        paramtypes.Add(par.Substring(s, e));

        IEnumerable<Type> @params = from type in paramtypes
                                    where !string.IsNullOrWhiteSpace(type)
                                    select FetchGenericType(type);

        string paramstring = string.Join(", ", from type in @params select "[" + type.FullName + ", " + type.Assembly.GetName().Name + "]");
        string result = @params.Count() == 0 ? cls : string.Format("{0}`{1}[{2}]", cls, @paramsCount(), paramstr);

        // The string has now the format '...List`1[[System.String, mscorlib]]'
        // or: 'System.Tuple[[System.Int32, mscorlib], [System.Object, mscorlib]]' ...
        return FetchType(result);
    }
    else
        return FetchType(typestring);
}

The function FetchType does some basic array bracket parsing and then fetches the type based on the resulted string by looking through the given assemblies.

Upvotes: 1

Jeff
Jeff

Reputation: 12785

I might be missing something, but it looks like you're looking for Reflection. Specifically, the GetType method. The documentation states the following about this method:

Gets the Type with the specified name, performing a case-sensitive search.

Then you will want to look at Activator.CreateInstance:

Creates an instance of the specified type using that type's default constructor

Before you create the instance for generic types, though, you'll be wanting to look at `Type.MakeGenericType':

Substitutes the elements of an array of types for the type parameters of the current generic type definition and returns a Type object representing the resulting constructed type.

You might find the answers to this other question useful too: How to use Activator to create an instance of a generic Type and casting it back to that type?

Upvotes: 2

Related Questions