Invoke method in stored object - c#

I'm a bit confused atm and i dont think it could be this hard as then i must be doing something wrong. What i am trying to do now for the past 2 days is to access a method inside an object that is stored in an List and i just cant get at it. In my mind it should just be to get the object back to its originated type and invoke the method but i just cant do it.
I been reading alot about Type, Generics and reflection but it cant get anyting to work so i am obviusly doing it all wrong and i need help finding the light!
Heres the latest code i have tried
Object customer = Hotel.Main.Manager.GetMainList(x);
Type frsttype = customer.GetType();
MethodInfo method = frsttype.GetMethod("GetCustomerSpecificData");
MethodInfo generic = method.MakeGenericMethod(frsttype);
String str = generic.Invoke(method);
What i am trying to reach is this method inside the object:
public override string GetCustomerSpecificData()
{
string strout = string.Format("{0,-5}{1,26}{2,28}{3,28}\a", ID, Name, Age, Gender);
string strInfo = Extra;
strout += (string.IsNullOrEmpty(strInfo) ? string.Empty : strInfo);
if (m_specialoffer)
{
strout += string.Format("\nSpecial Offer");
}
if (IsRegularCustomer)
{
strout += (IsDangerus ? "\nIs a regular customer " : "\nIs not a regular customer.");
}
strout += Environment.NewLine + PaymentInfo();
strout += (m_CarPark ? "\nHas car parked in garage." : "\nDoes not have car parked in garage.");
return strout;
}
I hope someone can point me in the correct direction as i dont think i am getting anywhere with this one :/
Any help and hints will be greatly appreciated!!! All will be upvoted for replies!
Regards

There a few things here that you need to do, firstly lets look at the codeyou posted
First question you need t ask youself is Do I need to use reflection, can I instead use interfaces or return a type that I know?
Do you have control of the GetMainList(x)? If so cant you change it so it returns something more useful other then a object?
Object customer = Hotel.Main.Manager.GetMainList(x);
Can you cast to anything?
Secondly your target method is no a generic method so the line below is not going to work.
MethodInfo generic = method.MakeGenericMethod(frsttype);
You are also invoking the method incorrectly you Invoke has two arguments the first one is the target object you wish to invoke the method against and the parameters you can pass into it.
Invoke(object obj, object[] parameters)
To invoke you method you need to the following.
Object customer = Hotel.Main.Manager.GetMainList(x);
Type frsttype = customer.GetType();
MethodInfo method = frsttype.GetMethod("GetCustomerSpecificData");
String str = method.Invoke(customer, null) as string;
There is some great questions and community wikis on stackoverflow and of course there is many tutorials and example in the MSDN library.
A nice tutorial for reflection in .net can be found below.
Reflection in C# Tutorial

i mean you can easy invoke it :
Type myType =customer.GetType();
MethodInfo method = typeof(customer).GetMethod("GetCustomerSpecificData");
MethodInfo generic = method.MakeGenericMethod(myType);
var res= generic.Invoke(this, null);

Closest to what you currently have this could work without relying on reflection.
Object customer = Hotel.Main.Manager.GetMainList(x);
string result="";
var custObj = customer as Customer;
if (custObj !=null)
{
result = custObj.GetCustomerSpecificData();
}
var specialcustObj = customer as SpecialCustomer;
if (specialcustObj !=null)
{
result = specialcustObj.GetCustomerSpecificData();
}
/* etc */
Or, If you can change the implementation of the different types in the List have an interface (or alternative an (abstract) base class.
/* alternatively name it ISpecificData if you want adhere common used standards */
public interface SpecificData
{
string GetCustomerSpecificData();
}
and for your Customer and other classes that can be in the list :
public class Customer:SpecificData
{
/* rest of implemementastion stays the same */
}
Your code to Get a customer would go like this, and will work for every object in the list that implemented the interface.
Object customer = Hotel.Main.Manager.GetMainList(x);
string result="";
var interfaceObj = customer as SpecificData;
if (interfaceObj != null)
{
result = interfaceObj.GetCustomerSpecificData();
}
When you know that only a specific interface will be in the list you can use the generic list to only hold object for that specific type:
mainlist = new List<SpecificData>();
and you can adapt GetMainList to only return the interface SpecificData

Related

Generic interface argument from string

I am trying to add generics to my DAL. There's a list of EF entities and their corresponding repositories. These repositories implement a generic interface. I can't figure out how to instantiantiate the repository.
public T Create(T dtoEntity)
{
string entityClassName = dtoEntity.GetType().Name;
string repositoryClassName = entityClassName + "Repository";
try
{
string entityFullName = entitiesNamespace + entityClassName;
IEntityBase entity = (IEntityBase)assembly.CreateInstance(entityFullName)!;
string repositoryFullName = repositoryNamespace + repositoryClassName;
Type myType = Type.GetType("SmartVisionERP.Dal.SqlServer.Master.DataModel.Config_Accounts,SmartVisionERP.Dal.SqlServer.Master")!;
// IEntityBaseRepository<myType> repository = (IEntityBaseRepository<myType>)assembly.CreateInstance(repositoryFullName)!
IEntityBaseRepository<Config_Accounts> repository = (IEntityBaseRepository<Config_Accounts>)assembly.CreateInstance(repositoryFullName)!;
var list = repository.GetList();
}
catch (Exception)
{
throw;
}
return dtoEntity;
}
I am receiving a dtoEntity, I'm extracting the class name and then build the repository name out of it. For the scenario I am testing, these are "Config_Accounts" and "Config_AccountsRepository".
I am using reflection to instatiate the EF entity, has the same name, it's located in a different assembly. This line works properly, I have the entity.
The repository interface expects a T. IEntityBaseRepository<T> where T : class, IEntityBase, new()
I am getting the correct type in myType variable.
The commented line fails with the message "myType is a variable but is used as a type".
As soon as I write Config_Accounts instead of myType, it works, but this defeats the goal, I am trying to pass the actual type there.
I am out of ideas. Could anyone shed some light? How can I pass to that line a type generated from a string, in such way it actually works?
Thank you
========= EDIT =========
Based on help I received I have changed the code to look like below. I got an error stating "cannot instantiate an interface", which makes sense, so I passed the base class instead. I got the repository in an object, but the object does not expose any of the methods defined in the base class. I am assuming those will need to be exposed and used through more reflection, as suggested in one of the answers.
public T Create(T dtoEntity)
{
string entityClassName = dtoEntity.GetType().Name;
string repositoryClassName = entityClassName + "Repository";
try
{
string entityFullName = $"{entitiesNamespace}{entityClassName}";
IEntityBase entity = (IEntityBase)assembly.CreateInstance(entityFullName)!;
Type template = typeof(EntityBaseRepository<>);
Type myType = Type.GetType("SmartVisionERP.Dal.SqlServer.Master.DataModel.Config_Accounts,SmartVisionERP.Dal.SqlServer.Master")!;
Type genericType = template.MakeGenericType(myType);
object instance = Activator.CreateInstance(genericType);
//string repositoryFullName = repositoryNamespace + repositoryClassName;
//IEntityBaseRepository<Config_Accounts> repository = (IEntityBaseRepository<Config_Accounts>)assembly.CreateInstance(repositoryFullName)!;
//var list = repository.GetList();
}
catch (Exception)
{
throw;
}
return dtoEntity;
}
Generic type arguments need to be known at compile time ( i.e. when jitted). To construct object of a generic type at runtime you need use reflection. I.e. something as this answer ( Thanks to Marco for the link)
Type template = typeof(IEntityBaseRepository<>);
Type genericType = template.MakeGenericType(myType);
object instance = Activator.CreateInstance(genericType);
Ofc, to use the created object you need to use more reflection, since you do not have any compile-time type. You might end up with your compile time type, T, eventually so you can return it, but keep in mind that you will use a fair amount of reflection, and reflection tend to only produce runtime errors, and these errors might not be trivial to decipher.
As an alternative, if the goal is to map some set of types to some other types, one to one, you might consider using some other pattern, for example the visitor-pattern. This will require more code since you need some mapping code for each entity type. But it has the advantage of being type safe. Some types of implementations can force the developer to add any corresponding mappings when a new type is added, and therefore reduce the risk of runtime errors.
Since I do not know your particular circumstance I cannot know what the best solution is. But whenever dealing with reflection it can be a good idea to take a moment to consider if the benefit is worth the loss of type safety.

How do I use the Type from my List<Type>?

I would like to make my code convention-based by using Types and keeping things simple, but generics has it's own complexity with it's own learning curve.
I have a bunch of POCOs (Plain Old CLR Objects) in a List that I'd like to iterate through later in the code.
var models = new List<Type>();
models.Add(typeof(Person));
models.Add(typeof(Company));
Would like to cycle through each list item:
models.ForEach(m =>
{
var label = m.FullName;
// var data = JsonConvert.DeserializeObject<List<typeof(m)>>(""); // doesn't work
var data = JsonConvert.DeserializeObject<List<m>>(""); // doesn't work either
...
}
The issue is that the "m" in the Deserialize line isn't working. What would be the best way to pass that through, i.e. making the 'List<m>' a 'List<T>' that we can use?
To use generics, you really need to know the Type (T) at compile time, you don't - you know it at run time. (Caveat: Its possible with reflection, but theres no need to use it when there's an overload as described below)
There is an overload of DeserializeObject which takes a Type rather than use generics. So your code would be
models.ForEach(m =>
{
var label = m.FullName;
var data = JsonConvert.DeserializeObject("",m);
...
}
However, as you've pointed out in comments you actually need a List<T> not a single T. You'll need a little bit of reflection, just to create the right type to pass to the above DeserializeObject call.
var tList = typeof(List<>); // Type of open List
models.ForEach(m =>
{
var label = m.FullName;
var tConvert = = tList.MakeGenericType(m);
var data = JsonConvert.DeserializeObject("",tConvert);
...
}
The answer to your question is above, but the more I look at it the harder it is to see what you can actually do with data. all you'll ever know about data is that it is an object. You cant cast it to anything - you wont know if its a list of Person or a list of Company.
Perhaps this was an overly contrived example you've used for a real-life problem. If not I forsee you're next problem is what to do with data!!
If you don't know the type at compile time you can do this with Reflection. Consider the following code:
models.ForEach(m =>
{
var mi = JsonConvert.GetType()
.GetMethod("DeserializeObject");
var m = mi.MakeGenericMethod(new[] { m });
// you pass [null] for the object because it's a [static] method
// and you don't have to declare [args] if you can express it simply
// but keep in mind that it's simply an object[]
m.Invoke(null, args);
}
Another solution is to call the generic method using reflection (if there isn't any overload that takes the type as parameter)
models.ForEach(m =>
{
MethodInfo method = typeof(JsonConvert).GetMethod("DeserializeObject");
MethodInfo generic = method.MakeGenericMethod(m);
generic.Invoke(null, "");
}

Calling static function via classname as a string in c#

My Problem
I have a Problem which i can not solve my self. I dont want to use so much code, because i have multiple Classes which extend another class (in my case its called "Data").
I have a log file, where each Data Group is beginning with a specific Group Name, for example "MitarbeiterSet". The abstract Data-Class is used to prefent to much code, where I implemented variables like "String[] data" (for the data beeing parsed from the log file e.g. < 101 4 3 6 3 30 80 2 0 0 1 300 >) or "static String parseInduction", which is used to determin, if this Class is the right one to create Objects from.
I have another Class, called ParseMonitor, which creates the StreamReader to parse the log-file. So if the right Class is found, i induct the setDataArray(StreamReader sr) function from the right Class, to parse the Data Array. (At this point i have to tell you, that i need those different Classes, because i need to upload them to a sql server specificly.)
This static function creates an object of it self and uses the parseLine(String line) Function to fill the object with data from the given line.
WHAT I NEED.
I want to call the static function of any class, just by having the name of this class. So i dont have to use that much code and be able to add more classes.
Later on i want to call every class and use the uploadToServer() to Upload it to the server.
Is this possible?
Since your static method is creating an instance of its class anyway, I suggest a different approach:
Create an interface that all classes that contain ParseLine can implement. (Change out the return type for the correct one):
public interface IParseLine
{
string ParseLine(string line);
}
Have all of the classes that contain ParseLine() implement IParseLine.
Create an instance of the class, cast it to an IParseLine, and execute the method:
IParseLine pl = Activator.CreateInstance(Type.GetType(className)) as IParseLine;
if (pl != null)
{
string parsedString = pl.ParseLine(line);
// ...
}
Edit From comments:
I want to create a while loop, which can be stated as followed:
while{!sr.EndofStream){ line = sr.ReadLine(); for(int i = 0; i <
classNames.length; i++){ if(line.Contains(classNames[i].MYINDICATOR){
CALL classNames[i] STATIC METHOD TO PARSE THE FOLLOWING LINES AND
CREATE DATA Objects of its Class } }
I didn't test this, but you can change the code to something like this (caching the reflection required to get MYINDICATOR):
IList<KeyValuePair<string, Type>> typeIndicators = classNames.Select(x => {
Type t = Type.GetType(x);
string indicator = (string)t.GetField("MYINDICATOR", BindingFlags.Public | BindingFlags.Static).GetValue(null);
return new KeyValuePair(indicator, t);
});
while (!sr.EndOfStream)
{
line = sr.ReadLine();
foreach (var types in typeIndicators)
{
if (line.Contains(types.Key))
{
IParseLine pl = Activator.CreateInstance(types.Value) as IParseLine;
if (pl != null)
{
string parsedString = pl.ParseLine(line);
}
}
}
}
I want to call the static function of any class, just by having the name of this class.
Well, you can use Type.GetType(className) to get a Type (note that the name needs to at least be fully qualified including the namespace, and may also need the assembly name depending on your exact scenario), then Type.GetMethod to get a MethodInfo. Finally, call MethodBase.Invoke to invoke the method.
If you could use typeof(Foo) instead of using a string, it would make the code simpler and more robust.
(Side-note: if your methods are really called parseLine, parseInduction, setDataArray etc, you should consider renaming them to follow .NET naming conventions :)
I think I see where you're coming from. In this simple exmaple below, I have a static class with a method in it (nothing amazing about that).
public static class MyStaticClass
{
public static DateTime GetTime()
{
return DateTime.Now;
}
}
If I want to invoke that method using reflection, I can just use the following code, but it does assume that the MyStaticClass class is available via a reference or inthe same project etc.
MethodInfo method = typeof(MyStaticClass).GetMethod("GetTime");
object result = method.Invoke(null, null);
if (result is DateTime)
{
Console.WriteLine(((DateTime)result).ToLongTimeString());
}
What you seem ot be asking for is a moethod of doing this when you don't have a reference to the class. In which case, try something like this:
MethodInfo method = Type.GetType("PocStaticReflect.MyStaticClass, PocStaticReflect, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null").GetMethod("GetTime");
object result = method.Invoke(null, null);
if (result is DateTime)
{
Console.WriteLine(((DateTime)result).ToLongTimeString());
}
Notice the fully qualified class name!
If you get that working, then you can simply loop though your class names and call the method you desire. Obviously, you'll probably want more error checking and more detail in the GetMethod() calls, but this shlud give you the gist of it. I've done something similar before looping though assemblies in a folder to pickup plug-ins for an application. That time, each of the classes implemented an interface to make them easier to locate, which may be helpful path to follow.
Or try this:
private static object GetResultFromStaticMethodClass(string qualifiedClassName, string method)
{
Type StaticClass = Type.GetType(qualifiedClassName);
MethodInfo methodInfo = StaticClass.GetMethod(method);
object result = methodInfo.Invoke(null, null);
return result;
}
Use:
object result = GetResultFromStaticMethodClass(
"Utilities.StringHelper,DaProject",
"ToList"
);
This call the static method ToList in the StringHelper class, in the Utilities namespace, in the DaProject project (same assembly and project name).
If you need parameters, add them in the second parameter in the methodInfo.Invoke(null, null) call

convert class string to class

I have the code below in my ASP.NET app, I would like to convert converterName variable to
Class and pass it to FillRequest<T> method.
Is it possible?
var converterName = HttpContext.Current.Items["ConverterName"] as string;
FillRequest<Web2ImageEntity>(Request.Params);
Alternatively I could do
var converterName = HttpContext.Current.Items["ConverterName"] as string;
if (converterName == "Web2ImageEntity")
FillRequest<Web2ImageEntity>(Request.Params);
but I have about 20 entity classes and I would like to find a way to write code as short as possible.
That would not be possible as the generic type needs to be specified at the compile time.
What you can do is change the FillRequest method to be something like below and then use reflection to do the desired task
FillRequest(string[] params,Type converter)
{
//Create object from converter type and call the req method
}
Or make the FillRequest take a Interface
FillRequest(string[] params, IConverter c)
{
//call c methods to convert
}
Calling this would be something like:
var type = Type.GetType(converterName);
FillRequest(Request.Params,(IConverter)Activator.CreateInstance(type));
Yes, take a look at Activator.CreateInstance():
var converterName = HttpContext.Current.Items["ConverterName"] as string;
var type = Type.GetType(converterName);
var yourObject = Activator.CreateInstance(type);
Be aware that the type must have a public parameterless constructor. Here is a link to the MSDN documentation; there are a bunch of overloads which might be useful to you:
http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx
UPDATE: If you must pass the object to a method with a generic type, then you will run into problems because the type is not known at compile time. In that case, I would consider having all of your converters implement a common interface, something like this:
var converterName = HttpContext.Current.Items["ConverterName"] as string;
var type = Type.GetType(converterName);
var yourObject = Activator.CreateInstance(type) as IMyConverter;
if (yourObject != null)
FillRequest<IMyConverter>(yourObject);
I found code idea here. Peter Moris pointed that he took code from Jon Skeets book, so if it will be useful - high five to Jon :)
create method:
public void DoFillRequest(Type type, string[] params)
{
MethodInfo methodInfo = this.GetType().GetMethod("FillRequest");
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(new Type[]{ type });
genericMethodInfo.Invoke(this, new object[]{ params });
}
and now call it:
var type = Type.GetType(converterName);
DoFillRequest(type, Request.Params);

Declare a generic type instance dynamically [duplicate]

This question already has answers here:
How do I use reflection to call a generic method?
(8 answers)
Closed 9 years ago.
Is it possible to declare an instance of a generic without knowing the type at design-time?
Example:
Int i = 1;
List<typeof(i)> list = new List<typeof(i)>();
where the type of i could be anything, instead of having to do:
List<int> list = new List<int();
If you don't know the type at compile-time, but you want the actual type (i.e. not List<object>) and you're not in a generic method/type with the appropriate type parameter, then you have to use reflection.
To make the reflection simpler, I've sometimes introduced a new generic type or method in my own code, so I can call that by reflection but then just use normal generics after that. For example:
object x = GetObjectFromSomewhere();
// I want to create a List<?> containing the existing
// object, but strongly typed to the "right" type depending
// on the type of the value of x
MethodInfo method = GetType().GetMethod("BuildListHelper");
method = method.MakeGenericMethod(new Type[] { x.GetType() });
object list = method.Invoke(this, new object[] { x });
// Later
public IList<T> BuildListHelper<T>(T item)
{
List<T> list = new List<T>();
list.Add(item);
return list;
}
Of course, you can't do an awful lot with the list afterwards if you don't know the type... that's why this kind of thing often falls down. Not always though - I've used something like the above on a few occasions, where the type system just doesn't quite let me express everything I need to statically.
EDIT: Note that although I'm calling Type.GetMethod in the code above, if you were going to execute it a lot you'd probably want to just call it once - after all, the method isn't going to change. You may be able to make it static (you could in the case above) and you probably want to make it private too. I left it as a public instance method for the simplicity of the GetMethod call in sample code - you'd need to specify the appropriate binding flags otherwise.
If you don't know the type at design-time, I'd say you have a list of OBJECTS (the base class for all other types).
List<object> list = new List<object>();
I think the best you are going to be able to do is something like this:
static void Main(string[] args)
{
int i = 1;
var thelist = CreateList(i);
}
public static List<T> CreateList<T>(T t)
{
return new List<T>();
}
You can also use Activator.CreateInstance. Example code snippet:
public class BaseRepository<T> where T : DataContext
{
protected T _dc;
public BaseRepository(string connectionString)
{
_dc = (T) Activator.CreateInstance(typeof(T), connectionString);
}
public void SubmitChanges()
{
_dc.SubmitChanges();
}
}
If you still want to type .Add(), .Remove(), do foreach etc. you can treat the List as a regular "old" System.Collections.IList,
since this interface is luckily implemented by List<T>.
And since all other posted answers to this question shows pretty much every other possible way to create an instance of a List<T> dynamically,
i will show one last way to do it.
I personally use this method when creating generic instances, when i don't really know nothing about the type at compile time,
and the type must be passed as a string, perhaps coming from the application configuration file.
In this example, T is System.String for simplicity but it could be anything:
Type T = typeof ( string ); // replace with actual T
string typeName = string.Format (
"System.Collections.Generic.List`1[[{0}]], mscorlib", T.AssemblyQualifiedName );
IList list = Activator.CreateInstance ( Type.GetType ( typeName ) )
as IList;
System.Diagnostics.Debug.Assert ( list != null ); //
list.Add ( "string 1" ); // new T
list.Add ( "string 2" ); // new T
foreach ( object item in list )
{
Console.WriteLine ( "item: {0}", item );
}

Categories

Resources