How to Get Generic Type from a String Representation

How can I get generic Type from a string representation?

The format for generics is the name, a ` character, the number of type parameters, followed by a comma-delimited list of the types in brackets:

Type.GetType("System.Collections.Generic.IEnumerable`1[System.String]");

I'm not sure there's an easy way to convert from the C# syntax for generics to the kind of string the CLR wants. I started writing a quick regex to parse it out like you mentioned in the question, but realized that unless you give up the ability to have nested generics as type parameters the parsing will get very complicated.

Get generic type from string value representation in different dlls

Both vcsjones and abatishchev has a correct answer, but you miss about the dlls.

Thanks to vcsjones i used this:

string strGenericType = "Namespace.ClassName`2, DllName";
Type template = Type.GetType(strGenericType);

Type genericType = template.MakeGenericType(new[] { type1, type2 });
object instance = Activator.CreateInstance(genericType);

Thanks to abatishchev this is shorter:

string strGenericType = "Namespace.ClassName`2[[Namespace1.Type1, Type1DllName],[Namespace2.Type2, Type2DllName]], DllName";
Type genericType = Type.GetType(strGenericType);

object instance = Activator.CreateInstance(genericType);

How to use string as generic type

Take a look at Type.GetType(string):

string name = GetClassName();

Type klass = Type.GetType("Namespace." + name); // Replace "Namespace." with all the namespaces the classes live in, as the argument to `Type.GetType()` should be a fully-qualified name
if (klass is null)
{
// Class was not found
}

Of course, you also need to create an instance of Generic<klass>:

Type genericOfKlass = typeof(Generic<>).MakeGenericType(klass);

Then instantiate it:

object instance = Activator.CreateInstance(genericOfKlass);

Then call .DoSomething() on it:

MethodInfo doSomething = genericOfKlass.GetMethod("DoSomething", BindingFlags.Public | BindingFlags.Instance);
doSomething.Invoke(instance, new object[] { });

As you can see, reflection (this kind of dynamic programming is called reflection) is not easy, but possible in .NET.

Edit: Complete example with request data:

Request request = GetClientRequst();

//container is IContainer of Autofac.

Type requestType = Type.GetType("Namespace." + requestType); // Replace "Namespace." with all the namespaces the classes live in, as the argument to `Type.GetType()` should be a fully-qualified name
Type entityDao = typeof(EntityDao<>).MakeGenericType(requestType);
MethodInfo containerResolve = container.GetType().GetMethod("Resolve");
MethodInfo genericContainerResolve = containerResolve.MakeGenericMethod(entityDao);
object resolveResult = genericContainerResolve.Invoke(container, new object[] { });
MethodInfo create = resolveResult.GetType().GetMethod("Create");
create.Invoke(resolveResult, new object[] { request.Data });

Having a string representation of a class name how do I pass it to a generic

See Andrew & Jon's answers on this question.

C# Reflection: How to get class reference from string

Someone should probably mark this as duplicate.

How to get a String representation of the type in a generic static method

You can't. The type is not available at runtime. Generic type parameters undergo a process called type erasure whereby the compiler erases the generic type as if it didn't exist, for backwards compatibility.

Get Class-object representation of generic parameter in Java

The reason why

Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];

works was because the superclass of this happens to be a class with a parameterised type as its superclass. Therefore you can get the actual type arguments of that type. The type parameters of superclasses are stored as metadata in the class file if you write them in the source file.

However, in your case, whatever is passed to the onCatch parameter is not going to have a superclass of Consumer<T>. After all, Consumer<T> is not a class! You need to use getGenericInterfaces and find the one that has the name that starts with java.util.function.Consumer.

System.out.println(
// I've assumed the Consumer interface is the first one, to keep it brief
((ParameterizedType)onCatch.getClass().getGenericInterfaces()[0])
.getActualTypeArguments()[0]
);

This would work if the caller calls onCatch like this:

onCatch(new Consumer<RuntimeException>() {
@Override
public void accept(RuntimeException e) {

}
});

The anonymous class will implement Consumer<RuntimeException>, and this information will be written to the class file representing the anonymous class.

However, if you use a lambda:

onCatch((RuntimeException e) -> {});

Then only a method like this is generated in the same class as the caller:

private static void lambda$caller$0(RuntimeException e) {

}

and at runtime, invokedynamic is used to create an instance that implements Consumer, and this is the bad news: the type parameter RuntimeException is not part of the generated class for this instance, for whatever reason.

The only way you can find RuntimeException now then, is if you somehow know who the caller is, and find the lambda$caller$0 method, and look at its parameter.

That said, everything I've wrote so far is pretty much all implementation detail, and I wouldn't use any of that in production code. I would say you should just add a Class<E> parameter:

onCatch(RuntimeException.class, e -> {});

It doesn't look that different on the caller's side anyway.

Get source code representation of generic type?

I've got this extension method, GetPrettyName(). This is basically it:

public static string GetPrettyName(this Type type)
{
var retval = type.Name;

if (type.IsGenericType)
{
var genargNames = type.GetGenericArguments().Select(t => GetPrettyName(t));
var idx = type.Name.IndexOf('`');
var typename = (idx > 0) ? type.Name.Substring(0, idx) : type.Name;
retval = String.Format("{0}.{1}<{2}>", type.Namespace, typename, String.Join(", ", genargNames));
}
else if (type.IsArray)
{
retval = GetPrettyName(type.GetElementType()) + "[]";
}
else if (String.IsNullOrEmpty(retval))
{
retval = type.Name;
}

return retval;
}

It operates recursively on each generic type parameter and builds out the full name in a format that's close to the code representation. It's good enough for our purposes (its just used in logging messages here). It can handle generics and arrays, but does not handle Entity Framework proxies that well.

How to get the simple generic type name?

You need to write simple class which recursively traverse type and generate simple name on each level:

class TypeSimpleName {

private final Type value;

public TypeSimpleName(Type value) {
this.value = value;
}

public String getName() {
return getName(value);
}

private String getName(Type type) {
if (type instanceof ParameterizedType) {
return getParameterizedTypeName((ParameterizedType) type);
}
if (type instanceof Class) {
return getClassSimpleName(type);
}
// handle other types if needed

return type.getTypeName();
}

private String getParameterizedTypeName(ParameterizedType type) {
StringBuilder builder = new StringBuilder();

builder.append(getName(type.getRawType()));
Type[] typeArguments = type.getActualTypeArguments();
if (typeArguments.length > 0) {
builder.append("<");
for (int i = 0; i < typeArguments.length; i++) {
Type arg = typeArguments[i];
builder.append(getName(arg));
if (i < typeArguments.length - 1) {
builder.append(", ");
}
}
builder.append(">");
}

return builder.toString();
}

private String getClassSimpleName(Type type) {
return ((Class) type).getSimpleName();
}

@Override
public String toString() {
return getName();
}
}

How to use it:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.util.ReflectionUtils;

public class Test {

public static void main(String[] args) {
System.out.println(getReturnTypeName("getList"));
System.out.println(getReturnTypeName("getListList"));
System.out.println(getReturnTypeName("getObj"));
System.out.println(getReturnTypeName("getMapMap"));
}

private static String getReturnTypeName(String method) {
Type returnType = ReflectionUtils.findMethod(Test.class, method).getGenericReturnType();

return new TypeSimpleName(returnType).getName();
}

public List<String> getList() {
return null;
}

public List<List<String>> getListList() {
return null;
}

public Integer getObj() {
return 1;
}

public Map<String, Map<Integer, BigDecimal>> getMapMap() {
return null;
}
}

Above code prints:

List<String>
List<List<String>>
Integer
Map<String, Map<Integer, BigDecimal>>


Related Topics



Leave a reply



Submit