Generic T from string in C# - c#

Sorry for my English Miserables.
I have 2 values ​​from an XML file and I need it to a generic method further lead to the return-value T. In one XML value is of this type, but how can I use this as a Type?
example:
var dataType = xml.Element("type").Value;
var modelList = await Mapper<dataType>(serviceXml, myNamespace, objType, serviceJson.ToString());

1) Load type with Type.GetType
2) Use reflection to create generic Mapper, i.e. typeof(Mapper<>).CreateGenericType(dataType)
3) Use Activator.CreateInstance to create instance of generic Mapper

You can't use a type variable as a parameter in a generic method. The type must be known at compile time. You can do it through reflection by calling a method created using the MakeGenericMethod call. I think it would look something like this:
var method = Mapper.GetType().GetMethod("DoesEntityExist")
.MakeGenericMethod(new Type[] { dataType });
method.Invoke(this, new object[] { serviceXml, myNamespace, objType, serviceJson.ToString() });
What you could do is create an interface or a base class and create an extension method to handle the conversion.

You can use the static GetType method that takes a string and return a Type if it can be found
Type.GetType("System.Collections.Generic.Dictionary`2[System.String,[MyType,MyAssembly]]")
If the type represented by the string cannot be found, it will return null
After getting the type, you can use the Mapper through reflection: if Mapper is a method then use the following
MethodInfo method = this.GetType().GetMethod("Mapper"); // this.GetType works if you aren't in a static method
MethodInfo generic = method.MakeGenericMethod(Type.GetType("theXmlValue"));
generic.Invoke(this, new object[] {/* your parameters to the Mapper method */});

Related

Construct a generic type through reflection given the type of another object

I'm passing a type to a method and creating an object based on that type. Assume argType is a Type and passed to the method as a parameter.
var assembly = AppDomain.CurrentDomain.GetAssemblies().Single(a => a.GetName().Name == "MyAssembly");
var newType = assembly.GetTypes().SingleOrDefault(x => x.FullName == argType.FullName + "Suffix");
var newObject = Activator.CreateInstance(newType);
This works fine for most objects, but if I pass a type with a generic sub type (e.g. MyClass<MyType>), it fails [Assume the goal is to set newObject to a MyClassSuffix<MyType>].
How could the code above be improved so that generic types (ending with "Suffix") can also be created? I looked at the FullName property for such types which contain substrings like '1[[. I'd rather not do regex parsing to append "Suffix" before these characters begin. I'm thinking there is a better way.
You can use MakeGenericType method if you have instance of both types i.e the generic class and the generic type parameter :
// assuming you have a generic class named Class<>
// taking one generic type parameter
Type generic = typeof(MyClassSuffix<>);
Type typeParameter = typeof(MyClass);
Type genericInstance = generic.MakeGenericType(typeParameter);
and then:
object o = Activator.CreateInstance(genericInstance);
In your case you have generic as newType and you would need to figure out which type you need to pass as type parameter and it should work.
Read the following MSDN docs for detailed instructions on this topic:
https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-examine-and-instantiate-generic-types-with-reflection
UDPATE:
You can get generic type parameters too following way
var newType = assembly.GetTypes().SingleOrDefault(x => x.FullName == argType.FullName + "Suffix");
if(newType.IsGenericType)
{
// Get the generic type parameters or type arguments.
Type[] typeParameters = t.GetGenericArguments();
// Construct the type Dictionary<String, Example>.
Type constructed = newType.MakeGenericType(typeParameters);
}
If you are having a closed type then you can instantiate it same way like a normal class. See the following example:
public class GenericClass<Int>
{
}
Now we can generate an instance of it like:
Type t = typeof(GenericClass<int>);
object o = Activator.CreateInstance(y);
Console.WriteLine(o);
DEMO Fiddle

How to check if method parameter type/return type is generic, in Roslyn?

As stated in the title, I want to check if a method params are generic + if the return type of a method is generic.
For example:
public ISet<string> Collect(MethodDeclarationSyntax method, SemanticModel semanticModel)
{
return method
.ParameterList
.Parameters
.Select(x => x.Type.ToString())
.ToImmutableHashSet();
}
Here I want to return all the types of the params for the method variable, that are not generic, but I can't find anything in the API to filter the results.
I have the same problem when checking if the return type of a method is generic.
It depends on what you have to work with. If you have an ArgumentListSyntax and thus zero or more ArgumentSyntaxes (ArgumentListSyntax.Arguments), you can get the type info from the argument expression:
var type = model.GetTypeInfo(argument.Expression).Type as INamedTypeSymbol;
And from there, the IsGenericType property. For example:
Debug.Assert(type.IsGenericType);
And if you have a MethodDeclarationSyntax object of the method, you can see if the ReturnType property is a type of GenericNameSyntax:
Debug.Assert(methodDeclaration.ReturnType is GenericNameSyntax);
cast to GenericNameSyntax to get more information about the generic type like type arguments.

Using Reflection.Emit to instantiate Generic Type with Generic Parameters

my goal is to use reflection emit to construct generic type with the generic parameters of the created generic method
so the end result of the created generic method is similar to
void DoSomeThing<T>(T arg){
var list=new List<T>();
}
so what I need is the code used to emit this fragment of code
new List<T>
and this is my try
var _assemblyName = "asm.dll";
var _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(_assemblyName), System.Reflection.Emit.AssemblyBuilderAccess.RunAndSave);
// ApplyReflectionPermission(asm);
var _moduleBuilder = _assemblyBuilder.DefineDynamicModule("module", _assemblyName, true);
var type = _moduleBuilder.DefineType("type");
var method = type.DefineMethod("DoSomeThing", MethodAttributes.Public | MethodAttributes.Static);
var genericPrms = method.DefineGenericParameters("T");
method.SetParameters(genericPrms);
method.SetReturnType(typeof(void));
var il = method.GetILGenerator();
var listType = typeof(List<>);
var list_of_T = listType.MakeGenericType(genericPrms);
il.DeclareLocal(list_of_T);
var c = list_of_T.GetConstructor(new Type[0]);
il.Emit(OpCodes.Newobj, c);
il.Emit(OpCodes.Stloc, 0);
il.Emit(OpCodes.Ret);
type.CreateType();
_assemblyBuilder.Save(_assemblyName);
the exception is in this line of code
var c = list_of_T.GetConstructor(new Type[0]);
and it is caused by this line of code
var list_of_T = listType.MakeGenericType(genericPrms);
the exception is
System.NotSupportedException: Specified method is not supported.
at System.Reflection.Emit.TypeBuilderInstantiation.GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
at System.Type.GetConstructor(BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
at System.Type.GetConstructor(Type[] types)
and by digging in (MakeGenericType) method, it returns a new instanse of TypeBuilderInstantiation if any of the parameter types isn't a (RuntimeType)
the type TypeBuilderInstantiation is nothing but an empty implementation of the abstract type 'TypeInfo' [whis is an abstract impl. of the type 'Type'], which all of its method throwing not supported exception
my goal is not to create a method to return new List, it's more complicated than that, but my obstacle is the same as doing so.
thanx for helping.
Yeah, there's definitely a trick to this. Indeed you cannot call any methods on the TypeBuilderInstantiation. Instead, TypeBuilder will let you get constructors for dependent types.
The GetConstructor method provides a way to get a ConstructorInfo object that represents a constructor of a constructed generic type whose generic type definition is represented by a TypeBuilder object.
https://msdn.microsoft.com/en-us/library/ms145822(v=vs.110).aspx
You first need the generic ConstructorInfo, gotten from typeof(List<>) in the usual way...
var listDefaultConstructor = listType.GetConstructor(new Type[0]);
and then instantiate it to your particular generic implementation:
var c = TypeBuilder.GetConstructor(list_of_T, listDefaultConstructor);
Whenever you would like to call a method on an instance of Type that represents an unconstructed/dependent type, instead look for a method with the same name in the Reflection.Emit hierarchy.
Among other things, this design pattern of passing in the generic version of the MethodInfo allows you to distinguish between calls to overloaded Class<T=int>.Foo(T) and Class<T=int>.Foo(int).

How do I use System.Type variable to call a generic method?

Type valueType = Type.GetType("int");
object value = new List<valueType>();
The first line compiles fine, But the 2nd does not.
How can I create a generic list (or call a generic method)
object value = foo<valueType>();
By only having a string representation of the type?
My end goal is actually to take two string "int" and "5 (as an example) and assign the value of 5 to the object [and eventually to the userSettings]. But I have a method that will convert "5" to the actual value if I can tell the generic method it is of type int based on the string representation.
T StringToValue<T>(string s)
{
return (T)Convert.ChangeType(s, typeof(T));
}
Update: I was thinking that creating a generic object and calling a generic method would use the same methodology, but I guess I was wrong. How can I call the generic method?
Type.GetType("int") returns null. This is invalid because int is just a keyword in the C# language, which is equivalent to the type System.Int32. It has no special meaning to the .NET CLR, so it's not usable in reflection. You might have meant typeof(int) or Type.GetType("System.Int32") (or it doesn't really matter, because that was just an example).
Anyway, once you have the right Type, this is how you can get your list. The key is MakeGenericType.
Type valueType = typeof(int);
object val = Activator.CreateInstance(typeof(List<>).MakeGenericType(valueType));
Console.WriteLine(val.GetType() == typeof(List<int>)); // "True" - it worked!
I will share an example from Jeffrey Richter's book CLR Via C# about constructing generic types, this is not specific to the question but will help guide you to finding the appropriate way of doing what you want:
public static class Program {
public static void Main() {
// Get a reference to the generic type's type object
Type openType = typeof(Dictionary<,>);
// Close the generic type by using TKey=String, TValue=Int32
Type closedType = openType.MakeGenericType(typeof(String), typeof(Int32));
// Construct an instance of the closed type
Object o = Activator.CreateInstance(closedType);
// Prove it worked
Console.WriteLine(o.GetType());
}
}
Will display: Dictionary`2[System.String,System.Int32]
try this:
Type valueType = Type.GetType("System.Int32");
Type listType = typeof(List<>).MakeGenericType(valueType);
IList list = (IList) Activator.CreateInstance(listType);
// now use Reflection to find the Parse() method on the valueType. This will not be possible for all types
string valueToAdd = "5";
MethodInfo parse = valueType.GetMethod("Parse", BindingFlags.Public | BindingFlags.Static);
object value = parse.Invoke(null, new object[] { valueToAdd });
list.Add(value);

passing static reflection information to static generic methods

EDIT: the class/method that i'm trying to run this inside is static and therefore i'm unable to pass this into the generic.Invoke
I have a static Data Access Class that i use to automatically parse data from various sources.
i was starting to re-factor it when i ran into a problem.
Im tring to pass a Type to a Generic method via reflection,
(the method then parses the type and returns the Type with a value)
my code currently looks like
Type type1 = typeof( T );
var item = (T)Activator.CreateInstance( typeof( T ), new object[] { } );
foreach (PropertyInfo info in type1.GetProperties())
{
Type dataType = info.PropertyType;
Type dataType = info.PropertyType;
MethodInfo method = typeof( DataReader ).GetMethod( "Read" );
MethodInfo generic = method.MakeGenericMethod( dataType );
//The next line is causing and error as it expects a 'this' to be passed to it
//but i cannot as i'm inside a static class
generic.Invoke( this, info.Name, reader );
info.SetValue(item,DataReader.Read<dataType>(info.Name, reader ) , null);
}
I guess DataReader.Read is the static method, right?
Therefore, change the error line like below, since you are calling the static method. There is not object, so you just pass null into Invoke method:
var value = generic.Invoke( null, new object[] {info.Name, reader} );
The type parameter to a generic method isn't an instance of Type; you can't use your variable in this way. However, you can use reflection to create the closed-generic MethodInfo you require (that is, with the type parameter specified), which would look something like this:
// this line may need adjusting depending on whether the method you're calling is static
MethodInfo readMethod = typeof(DataReader).GetMethod("Read");
foreach (PropertyInfo info in type1.GetProperties())
{
// get a "closed" instance of the generic method using the required type
MethodInfo genericReadMethod m.MakeGenericMethod(new Type[] { info.PropertyType });
// invoke the generic method
object value = genericReadMethod.Invoke(info.Name, reader);
info.SetValue(item, value, null);
}

Categories