Reputation: 2963
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.:
System.Tuple<string, System.Action>
T
like int[]
, System.IntPtr[]
, etc...T
like int*
, char**
, Namespace.MyStruct*
, etc...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
Reputation: 2963
I solved it (temporarily) as follows:
<
or >
ParseGeneric
) and continue recursively using the parsed tokens[]
, [][]
, [,]
etc.)Array::CreateInstance(Type,int[])
*
) and parse the type accordingly using Marshal::StructureToPtr
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
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