I have the following C# Code. A Base class and classes that inherit from that base class.
I use this Baseclasses in an special List. This List also has the Member ReadListAsXmlAs.
public class ResultSetBase
{
some Members
}
public class ResultSetBaseSweep : ResultSetBase
{
some other Members
}
public class ResultList<T> where T : ResultSetBase
{
public ResultList<T> ReadListAsXmlAs(params string[] path)
{
...
}
}
In an other methode I want to create an dynamic object of the Type ResultList. I know of which class the ResultList is, only at runtime. (e.g. ResulstSetBaseSweep, or any other inherited from ResultSetBase).
I create an dynamic Object of this Type. the following way.
Type myType = Type.GetType("Class in String Format");
Type listtype = typeof(ResultSaver.ResultList<>).MakeGenericType(myType);
object resultlist = Activator.CreateInstance(listtype);
Now i need to call the Methode ReadListAsXmlAs. As it is of type object, the compiler complains when when i try to call
resultlist.ReadListAsXmlAs(...);
So I tried to call it over Reflections:
myType.InvokeMember("ReadListAsXmlAs", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, resultlist, new object[] { filenames.ToArray() });
Then I get the compiler error: ReadListAsXmlAs not found! How is it done right?
I have found a solution to the problem:
Type myType = Type.GetType(LstBoxClass.SelectedItem.ToString());
Type listtype = typeof(ResultSaver.ResultList<>).MakeGenericType(myType);
object resultlist = Activator.CreateInstance(listtype);
MethodInfo method = listtype.GetMethod("ReadListAsXmlAs");
method.Invoke(resultlist, new Object[] {filenames.ToArray()});
Related
I have a generic class that has a generic method that uses the same type as the type passed when instantiating the object. At runtime, I will only know the name of the object I need to pass in by a string representation of that object name. I've read a few things about using Activator and possibly using dynamic but I can't wrap my head around how I need to make this work. Here's a snippet of what my generic class looks like:
public class MyClass<T> where T : class, new()
{
public IList<T> MyMethod(Stream stream)
{
var data = new List<T>();
// stuff to create my list of objects
return data;
}
}
I need to return my IList from the MyMethod() method based on the name of the object I'm passing in as a string.
I could just do a switch/case on the string and then instantiate the MyClass within the case with the reference to the "real" object, but I'm wondering if there's a better (shorter and cleaner) way of doing this.
TIA
Your wrapper got the following signature:
public class MyClass<T> where T : class, new()
it basically says "T needs to be a class and have a default constructor". The interesting part is about the default constructor. It means that the class must have a constructor with no arguments.
It tells .NET that you can invoke:
var obj = new T();
So the first step is to do just that:
public class MyClass<T> where T : class, new()
{
public IList<T> MyMethod(Stream stream)
{
var data = new List<T>();
//this
var obj = new T();
return data;
}
}
next you wanted to invoke a method. That's done with the help of reflection.
A simple example is:
var obj = new T();
//get type information
var type = obj.GetType();
//find a public method named "DoStuff"
var method = type.GetMethod("DoStuff");
// It got one argument which is a string.
// .. so invoke instance **obj** with a string argument
method.Invoke(obj, new object[]{"a string argument"});
Update
I missed the important part:
I need to return my IList from the MyMethod() method based on the name of the object I'm passing in as a string.
If the type is declared in the same assembly as your executing code you can just pass the full type name like Some.Namespace.ClassName" toType.GetType()`:
var type = Type.GetType("Some.Namespace.ClassName");
var obj = Activator.CreateInstance(type);
If the class is declared in another assembly you need to specify it:
var type = Type.GetType("Some.Namespace.ClassName, SomeAsseblyName");
var obj = Activator.CreateInstance(type);
The rest is pretty much the same.
If you only have the class name you can traverse the assembly to find the correct type:
var type = Assembly.GetExecutingAssembly()
.GetTypes()
.FirstOrDefault(x => x.Name == "YourName");
var obj = Activator.CreateInstance(type);
It sounds like you want to create the generic type so that you can create an instance of it.
//Assuming "typeName" is a string defining the generic parameter for the
//type you want to create.
var genericTypeArgument = Type.GetType(typeName);
var genericType = typeof (MyGenericType<>).MakeGenericType(genericTypeArgument);
var instance = Activator.CreateInstance(genericType);
This assumes that you already know what the generic type is, but not the type argument for that generic type. In other words, you're trying to determine what the <T> is.
Use Reflection. Make MyMethod static. See the code below:
public object run(string typename, Stream stream)
{
var ttype = Assembly
.GetExecutingAssembly()
.GetTypes()
.FirstOrDefault(x => x.Name == typename);
MethodInfo minfo = typeof(MyClass)
.GetMethod("MyMethod", BindingFlags.Static | BindingFlags.Public);
return minfo
.MakeGenericMethod(ttype)
.Invoke(null, new object[] { stream });
}
I have a generic class with a generic method:
public class GenericClass<T> where T : class
{
public void GenericMethod<T>(T item)
{
// do work here
}
}
I also have an object with multiple properties, some of which can be other objects:
public class TestObject
{
public ChildObject ChildObject { get; set; }
}
I am then attempting to use a generic method which will reflect through TestObject and call GenericMethod for all properties that are in and of themselves custom classes such as ChildObject (I do have a way of determining this based on inheritance, however for the sake of keeping it simple did not include that code for this example):
public void ReflectingMethod<T>(T item)
{
var properties = item.GetType().GetProperties();
foreach (var property in properties)
{
var type = property.PropertyType;
dynamic propertyModel = property.GetValue(model, null);
var castedObject = Convert.ChangeType(propertyModel, type);
var genericClass = Activator.CreateInstance(typeof(GenericClass<>).MakeGenericType(type));
var method = genericClass.GetType().GetMethod("GenericMethod", new [] { type });
method.Invoke(castedObject, null);
}
}
The problem is that whether I attempt to change property's type (as shown in the above example) or I pass property directly to method.Invoke, such as:
method.Invoke(propertyModel, null);
I still receive the same error:
Object does not match target type.
At RunTime method is:
GenericMethod(TestProject.ChildObject)
And castedObject is:
TestProject.ChildObject
I am confused as to why I am getting the error I am, when it would appear that the casted object is exactly of the type that the method is looking for.
EDIT 1
Included the call to GetValue that I had originally left out when posting the question.
The call
method.Invoke(castedObject, null);
is incorrect. It should be
method.Invoke(genericClass, new object[] { castedObject });
Since you are trying to call an instance method, the first argument to Invoke(object, object[]) must be the this instance. For static method, pass null as first argument. Method arguments are always passed via the second object[] argument of the Invoke method.
I can create a generic class that takes, as its template parameter, a C# type, and then within the generic class use the System.Type information corresponding to that C# type:
public class Generic<T>
{
public bool IsArray()
{
return typeof(T).IsArray();
}
public T Create()
{
return blah();
}
}
Generic<int> gi = new Generic<int>();
Debug.WriteLine("int isarray=" + gi.IsArray());
Generic<DateTime> gdt;
But now let's say what I have, is a System.Type. I can't use this to instantiate my generic class:
FieldInfo field = foo();
Generic<field.FieldType> g; // Not valid!
Is there some clever C# thing I can do, to convert a System.Type back to the original C# type? Or some other way, to create a generic that can (1) give me information about the System.Type, and (2) create objects of the associate C# type?
By the way, this is a very contrived example to explain the problem I'm trying to solve, don't worry too much about whether Generic makes sense or not!
The only thing you can do is use reflection. This because while the int of Generic<int> is known at compile-time, the field.FieldType is known only at runtime.
Reflection example:
Type type = typeof(Generic<>).MakeGenericType(field.FieldType);
// Object of type Generic<field.FieldType>
object gen = Activator.CreateInstance(type);
But even here, from a Type (field.FieldType) you obtain another Type (type)
There are normally three ways of using this:
Full reflection: you use the object of type Generic<type> only through reflection. You create it through Activator.CreateInstance and from there you begin using Type.GetMethod() and Invoke()
Type type = typeof(Generic<>).MakeGenericType(field.FieldType);
// Object of type Generic<field.FieldType>
object gen = Activator.CreateInstance(type);
MethodInfo isArray = type.GetMethod("IsArray");
bool result = (bool)isArray.Invoke(gen, null);
Interfaces/base classes: you have a non-generic base class or interface that is common between all the Generic<T>. You use your object only though that interface/base class.
public class Generic<T> : IComparable where T : new()
{
public bool IsArray()
{
return typeof(T).IsArray;
}
public T Create()
{
return new T();
}
public int CompareTo(object obj)
{
return 0;
}
}
Type type = typeof(Generic<>).MakeGenericType(field.FieldType);
IComparable cmp = (IComparable)Activator.CreateInstance(type);
int res = cmp.CompareTo(cmp);
A generic method where you put all the handling of the Generic<T>. That is the only method that is used through reflection.
public static void WorkWithT<T>() where T : new()
{
Generic<T> g = new Generic<T>();
T obj = g.Create();
Console.WriteLine(g.IsArray());
}
var method = typeof(Program).GetMethod("WorkWithT").MakeGenericMethod(field.FieldType);
// Single reflection use. Inside WorkWithT no reflection is used.
method.Invoke(null, null);
internal interface Rule {
}
private class Rule<T> : Rule {
//Properties & Other Stuff
}
void method() {
//For simplicity I used string here. It can be anything when the code is in context
Type _type = typeof(string);
Rule[] _rules;
//This is causing an error because _type could not be found
_rules = new Rule<_type>[] { };
}
Is it even possible to instantiate a class with a generic type that is stored in a variable?
-- EDIT
From my first example I thought I would be able to apply the same concept for calling a method. But it seems that I was wrong. I'm trying to use newtonsoft json library to deserialize a string as a generic type. I found this question that allows you to invoke a method with a generic type. I looked around on how to cast the object to _foo but could only find casting where the type is known. Any idea?
Using Newtonsoft.Json;
void method() {
//For simplicity I used string here. It can be anything when the code is in context
Type _type = typeof(string);
Rule[] _rules;
// ------------------ New Additions ----------------
var _foo = (Rule[])Array.CreateInstance(typeof(Rule<>).MakeGenericType(_type),0);
MethodInfo method = typeof(JsonConvert).GetMethod("DeserializeObject");
MethodInfo generic = method.MakeGenericMethod(_foo.GetType());
//How do you cast this to a type _myType?
_rules = generic.Invoke(this, new[] { "JSON Data" });
}
It's possible, but you have to use reflection:
Type genericTypeDefinition = typeof(Rule<>);
Type genericType = genericTypeDefinition.MakeGenericType(_type);
Rule[] array = (Rule[])Array.CreateInstance(genericType, 0);
I've got this class with a method that returns an object:
public class Deserializer<T>
{
public static T FromJson(string json)
{
return new JavaScriptSerializer().Deserialize<T>(json);
}
}
And I've got a Type. How do I create an instance of the Deserializer class based on a this type? The following obviously doesn't work:
var type = typeOf(MyObject);
var foo = Deserializer<type>.FromJson(json);
You could provide a non-generic version as well for consumers that don't know the type at compile-time (which is a good practice by the way when you are exposing an API):
public class Deserializer
{
public static T FromJson<T>(string json)
{
return new JavaScriptSerializer().Deserialize<T>(json);
}
public static object FromJson(string json, Type type)
{
return new JavaScriptSerializer().Deserialize(json, type);
}
}
Now, consumers that know the type at compile-time:
Foo foo = Deserializer.FromJson<Foo>(json);
and consumers that don't know the type at compile-time:
Type type = ...
object instance = Deserializer.FromJson(json, type);
This is how you could do it, but it's not exactly optimal unless you absolutely must do it this way. Go with one of the other answers if you can!
dynamic result = typeof(Deserializer<>).MakeGenericType(type)
.InvokeMember("FromJson", BindingFlags.Public | BindingFlags.Static |
BindingFlags.InvokeMethod, null, null,
new [] { json });
You can't do this. Generic type parameters are statically bound (compile-time). You can't pass it a System.Type object.
For your simple example, You could (somewhat obviously) just use MyObject:
MyObject foo = Deserializer<MyObject>.FromJson(json);
If you could do this, what would the resultant type of FromJson be? How would you use that object?
Type t = ___; // How do you get this?
??? foo = Deserializer<t>.FromJson(json); // What datatype is foo?
What you're trying to do isn't a feature of a statically-typed language like C#. It's more of a dynamic language feature, like Python, where you just return whatever you want and do whatever you want to it.