11

Within a windows app, using C#, I have a reporting module that will be reliant upon classes to populate the reports. However there will be many reports and I do not want to have to code for each one.

The flow will be as such: Within the report editor, the report will be assigned a class (i.e. "Applications") as a string. When the user selected the report to run, the code will acquire the data from a SQL query. The code will take the data and find out which class to place the data into. Then the report will take the class and populate the report with the data from the class.

Here is my dilemna, how do I make the code dynamic so that the code will convert the assigned class into the proper Class Object?

Example in mind:

gVar = Report;
(gVar.ReportClass)oClass = new gVar.ReportClass;
Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
mattgcon
  • 4,768
  • 19
  • 69
  • 117
  • Thanks Anthony I forgot to put that last part into code tags – mattgcon Feb 02 '11 at 16:00
  • Im not the same person who posted that question, I didn't know how to properly word my question so that didn't pop up – mattgcon Feb 02 '11 at 16:03
  • Thats not a problem matt. I just added the duplicate so these can be linked. I would check out that question. It should answer your question (if I understand it correctly). – Kyle Trauberman Feb 02 '11 at 16:05

4 Answers4

12

Use Type.GetType (specifically one of the overloads (e.g., Type.GetType(string)) that takes a string parameter) to load the instance of Type for the appropriate class, and then use Activator.CreateInstance or Type.GetConstructor on that instance of Type to instantiate an instance.

So, something like

Type type = Type.GetType(assemblyQualifiedName);
object instance = Activator.CreateInstance(type);

Note that you must pass the assembly qualified name unless the type is in mscorlib or the currently executing assembly.

Additionally, Activator.CreateInstance assumes the existence of a default constructor. If there is not a default constructor, or you need to pass some parameters to the constructor, you will have to use an overload of Activator.CreateInstance that lets you specify the constructor parameters, or Type.GetConstructor to load the appropriate constructor.

jason
  • 236,483
  • 35
  • 423
  • 525
  • here is a question though that just popped into my head, when I do instatiate the class from the string, I am guessing there is no way to place the data into the respective properties within the class dynamically. (i.e reportclass.clientname = dt["CLIENTNAME"].ToString()) – mattgcon Feb 02 '11 at 16:11
  • If you _know_ that your object will have such a property (say because all of the objects that you are dynamically instantiating derive from a common base class or a common interface, you could cast to that base class or interface and then access the property through that base class or interface). So say all of your objects derive from a class named `Report` and `Report` has a property named `ClientName`. Then you could say `Report r = (Report)Activator.CreateInstance(type); r.ClientName = dt["CLIENTNAME"].ToString();`. Alternatively, you could use reflection to set the property. Make sense? – jason Feb 02 '11 at 16:20
  • Hmmmm, thats an issue then because there is not common base class or common interface and from what I remember about reflection I would have to tell it what properties I am looking for by referencing it in a string. – mattgcon Feb 02 '11 at 16:25
  • @mattgcon: Yes, that's how you would set a property using reflection. If you have a convention like there is always a property on the object that matches (ignoring case) the value of string being passed to the indexer of `dt`, then you can easily do this. – jason Feb 02 '11 at 16:27
4

You can use reflection to do it. If you give them all some similar base class or interface, you can do something like:

myBaseReport report = (myBaseReport)System.Activator.CreateInstance("MyAssemblyName", myClassStringWithFullNameSpace).Unwrap();

This will go into the assembly named and load the class directly. The class string is the full name of the type in question, so something like MyGlobalNamespace.MyCustomNameSpace.MySpecificType. This will allow you to create the specific type of report and put it into the base class type or interface type.

Joel Etherton
  • 37,325
  • 10
  • 89
  • 104
  • Unfortunately, no two report classes are the same, so a base class or interface was not created. – mattgcon Feb 02 '11 at 16:08
  • @mattgcon - Then you'll be better of typing as an object when performing this kind of instantiation. And any methods you pass to will need to accept object. – Joel Etherton Feb 02 '11 at 16:13
  • @JoelEtherton : I voted up because you mentioned class name WITH FULL NAMESPACE whcih makes it clear to use. – Ram Jan 04 '13 at 06:30
2

You'll be fine using implicit operators:

That sounds good for your needs, because it allows you to do something like:

Orange orange = new Orange();
Apple apple = (Apple)orange;

Or:

string appleJson = "{ Weight: '2kg' }";
Apple apple = appleJson;

An implicit operator will deserialize that apple-JSON-serialized string into a regular Apple-typed object.

I don't know if this is what you're looking for, and I hope no one will be voting down if this is a C# feature that's available.

EDITED: I misunderstood the question. Thanks to commenters, even who down-voted my answer, because I was wrong.

EDIT 2:

Taking others Activator/Reflection approach - which seems to be the right one for the author of current question -, and reading his other question about "how to fill then the properties of obtained report instance", I want to suggest some solution.

You can define some attribute like "ReportPropertyAttribute" with no properties, default constructor, and inspect with reflection for properties that are marked with suggested attribute.

Or you can define some configuration file or configuration section (regular .NET configuration API), so you can define "known report types" and "which properties are arguments or parameters of some report type".

I hope this is in the line of your needs!

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • This is assuming he knows the type already. The way the question reads to me is the type can vary and he won't know the actual type until runtime. This won't work for that. – Kyle Trauberman Feb 02 '11 at 16:08
  • Well, with the fewer info he provided, I tried to think he's in the other situation, because he says "I don't want to repeat the code of converting A to B". – Matías Fidemraizer Feb 02 '11 at 16:10
  • I did say " the report will be assigned a class (i.e. "Applications") as a string" and "how do I make the code dynamic so that the code will convert the assigned class into the proper Class Object?" – mattgcon Feb 02 '11 at 16:14
  • Right, now I've double-checked your text, and I understood the problem. I believe reflection/activator approach suggested by others is the right way, or maybe, you can use inversion of control. – Matías Fidemraizer Feb 02 '11 at 16:15
  • I am going to use activator, however I am confused as to after what do I do to populate the class object with the data since it will be all done at run time – mattgcon Feb 02 '11 at 16:16
  • Well, you'll need to use reflection to fill such properties. For that reason, I believe that you need to think about using attributes or some configuration scheme so you can retrieve which properties should be filled in the obtained object. – Matías Fidemraizer Feb 02 '11 at 16:18
  • You should delete this answer, as it provides no lasting value. – Kyle Trauberman Feb 02 '11 at 16:21
  • Wait, @Kile! I've not been defeated yet! Just joking, I want to help in answering this question, read my update. Thanks. – Matías Fidemraizer Feb 02 '11 at 16:23
  • I removed my downvote. Since you added information to the answer, Its no longer unnecessary. :) – Kyle Trauberman Feb 02 '11 at 16:24
  • I removed mine as well. doesnt reflection require me to hand code the properties of the class into the code so it can look for them? – mattgcon Feb 02 '11 at 16:26
  • Yes, but if you use attributes, you'll infer that names with not-hardcoded names (aka string literals, which is an anti-pattern in this case). – Matías Fidemraizer Feb 02 '11 at 16:28
  • I've to go sorry, I hope you get the final word to get solved your problem, I go home (I was @ work). Good luck!!!!!!!!!! – Matías Fidemraizer Feb 02 '11 at 16:29
  • You should use a factory method to build your report objects based on the type. (assuming you have a limited number of different possible report types). Or maybe have each report implement an interface that specifies a `BuildReport` method that sets the necessary properties based on the input. – Kyle Trauberman Feb 02 '11 at 16:29
  • that sounds like it would work, but I am still in the beginning intermediate level of C# and have no idea what a factory method is or how to even do it. I will have to go and investigate that. – mattgcon Feb 02 '11 at 16:31
  • Well if I could use reflection or activator to get the class couldnt I use the same to get each property within the class? – mattgcon Feb 02 '11 at 16:32
  • Yes, you can. General rule of thumb is to avoid reflection as much as possible though. Reflection is extremely slow, so if you can set the properties in a non-reflection way, you should see a performance increase. – Kyle Trauberman Feb 02 '11 at 16:34
  • Also, the code to find the properties and set the values can get complex. My recommendation is to use an interface to provide a method to build the report on each report class. – Kyle Trauberman Feb 02 '11 at 16:35
  • oh gotcha, hmmm I wanted to make the creation of reports within the system as easy as possible but if I have to create an interface then i guess I have to figure something out. – mattgcon Feb 02 '11 at 16:36
  • I'm not that sure that reflection could be that performance devil thing in in this case. Metadata with attributes-based programming needs reflection, there's no choice. And forcing to implement an interface, derive some class isn't always the best solution, and I believe that this is the case. – Matías Fidemraizer Feb 02 '11 at 19:06
2

It's a little unclear exactly what you're asking for here. Based on what I"m reading, though, you have a string that contains a type name and you want to instantiate a class based on that? You can use reflection to do this...

Type type = Type.GetType(strTypeName);
object oClass = Activator.CreateInstance(type);
Steve Danner
  • 21,818
  • 7
  • 41
  • 51