0

I have a number of classes with the following pattern:

namespace MyCompany.MyApplication.ReportingClasses
{
public class ReportingClassName
{
    public string HTMLReportString {get; private set;}

    ReportingClassName()
    {
        // Use linq to generate report;
        // Populate gridview, pass object to function which returns HTMLString;
        // set HTMLReportString property;
    }

}
}

Each class holds a different linq query based on the report. I want to load the class dynamically from a list of reports in a drop down box. I store the AsseblyQualifiedName as well as a display name to populate the DDL. I have used reflection based on the posts that I have seen, but I can't seem to perform what I would like;

string myAssembly = "AssemblyName"; // This is static;
string myClass = "AssemblyQualifiedName"; // This value from DDL;
var myObject = Activator.CreateInstance(AssemblyName, AssemblyQualifiedName);

string propertyValue = myObject.HTMLReportString;

"UpdatePanelID".InnerHTML = propertyValue;

Is what I am trying to accomplish possible?

UncleJasper75
  • 69
  • 3
  • 13
  • 1
    The first four lines are straightforward - change `var myObject` to `dynamic myObject` and it should be fine. I've no idea what you're trying to do with the last line though... – Jon Skeet Nov 04 '14 at 16:05
  • Normally would be duplicate of http://stackoverflow.com/questions/10338018/how-to-get-a-property-value-using-reflection, but now-days using `dynamic` is better option of this particular case. – Alexei Levenkov Nov 04 '14 at 16:19

2 Answers2

2

myObject is of type object, so obviously it doesn't have any property named HTMLReportString.

Since you don't know the type of myObject at compile time, you'll have to either:

  1. use reflection to invoke the property

    string value = (string) myObject.GetType()
                                    .GetProperty("HTMLReportString")
                                    .GetValue(myObject); 
    
  2. use dynamic typing

    dynamic myObject = //...
    string value = myObject.HTMLReportString;
    
dcastro
  • 66,540
  • 21
  • 145
  • 155
  • Is there a difference in IL between these two cases? – Dialecticus Nov 04 '14 at 16:32
  • @Dialecticus Yes, the second code will actually only be compiled at runtime by the [DLR](http://msdn.microsoft.com/en-us/library/dd233052(v=vs.110).aspx). I'm not sure which would have the best performance, you'd have to test this yourself. – dcastro Nov 04 '14 at 16:35
2

In addition of dcastro answer's (which is good), I would like to suggest a third solution, which looks much cleaner to me : as "ReportingClassName" is your own code, you could modify it to make it realize an interface which provides what you need :

namespace MyCompany.MyApplication.ReportingClasses
{
public interface IReporting
{
    string HTMLReportString {get;}    
}

public class ReportingClassName : IReporting
{
    public string HTMLReportString {get; private set;}

    ReportingClassName()
    {
        // Use linq to generate report;
        // Populate gridview, pass object to function which returns HTMLString;
        // set HTMLReportString property;
    }

}
}


string myAssembly = "AssemblyName"; // This is static;
string myClass = "AssemblyQualifiedName"; // This value from DDL;
var myObject = Activator.CreateInstance(AssemblyName, AssemblyQualifiedName);

string propertyValue = ((IReporting)myObject).HTMLReportString; // Thanks to the interface, myObject provides HTMLReportString and it doesn't need reflection neither "dynamic".

"UpdatePanelID".InnerHTML = propertyValue;

For the last part, you could also do :

string propertyValue; 
var myReport = myObject as IReporting
if(myReport != null)   
{ 
    propertyValue = myReport.HTMLReportString; 
}
else 
{ 
    // Handle the error  
}

Just to be safer.

AFract
  • 8,868
  • 6
  • 48
  • 70
  • 2
    +1, I forgot to mention this. If you can modify these classes, make them all implement a common interface. – dcastro Nov 04 '14 at 16:37
  • Thanks dcastro, I did implement the the common interface on all objects. This also taught me quite a bit about the usefulness of Interfaces, so that is awesome. Richard, I marked this answer as correct, but there was one oversight, (IReporting)myObject should be (IReporting)myObject.Unwrap(). Once I added that method, it worked as advertised! I edited your response to reflect that change so the entire solution would be contained in the body of the response instead of in the comments. I appreciate all the help! – UncleJasper75 Nov 04 '14 at 17:29
  • "Unwrap()" ? Where does this come ? Activator.CreateInstance returns an instance of the given type. Unwrap has no meaning in this case. I'm not sure to understand what you're talking about. But of the course the most important is that you got what you want. – AFract Nov 04 '14 at 17:37
  • When I implemented the solution you gave I received an error (Paraphrased) "Can not convert type ObjectHandler to type IReporting." I searched for the error and found this post [link](http://stackoverflow.com/questions/13366352/unable-to-cast-system-runtime-remoting-objecthandle). Based on that post I implemented the Unwrap() method and it cast perfectly. I hope I didn't offend with my edit. Your answer really did solve my issue. – UncleJasper75 Nov 04 '14 at 18:17
  • 2
    @UncleJasper75 is right - this [specific overload](http://msdn.microsoft.com/en-us/library/d133hta4(v=vs.110).aspx) of `Activator.CreateInstance` returns an `ObjectHandle` that needs to be unwrapped, not an `Object`. I must admit I was surprised myself, as *most* other overloads of this method, such as [this one](http://msdn.microsoft.com/en-us/library/wccyzw83(v=vs.110).aspx), return an `object` - which doesn't need unwrapping. – dcastro Nov 04 '14 at 22:12
  • @dcastro : never noticed this before. Thank you :). – AFract Nov 05 '14 at 09:24
  • @UncleJasper75 : no pb, of course. Please not that I kept the method you called yourself in your original question, assuming it returns an instance of the expected object. Glad to see my answer helped ! – AFract Nov 05 '14 at 09:26