开发者

C#: Dynamically instantiate different classes in the same statement?

Here is a simplified version of what I'm trying to do:

Without having multiple if..else clauses and switch blocks, can I mimic the behavior of Javascript's eval() shudder to instantiate a class in C#?

// Determine report orientation -- Portrait or Landscape
// There are 2 differently styled reports (beyond paper orientation)

string reportType = "Portrait";
GenericReport report;
report = new eval(reportType + "Report()");  // Resolves to PortraitReport()

The need stems from the fact that I have 6 types of Crystal Reports (that do the same thing, but look drastically different) for 50 states. There are 3 styles each, rather than entertain the notion of a giant switch block with nested开发者_运维问答 if..else statements determining which of 900 reports to use, I was hoping for an eval-like solution.


You could use Activator.CreateInstance("myAssembly", "PortrainReport");. Although the more readable way would be to create a Portrait Factory, which would create the correct type for you.


As people specified above, you can use Activator class to create an instance of the class by its text name.

But, there is one more option. When you told about using eval like function in c# i assumed, you not only want to create an instance of the class by its text name, but also fill it with properties from the same string.

For this purpose you need to use deserialization.

Deserialization converts string like representation of the class into its instance and restoring all its properties that was specified in the string.

Xml serialization. Its using XML file for converting into instance. Here is small example:

public class Report1 
{
 public string Orientation {get;set;}
 public string ReportParameter1 {get;set;}
 public string ReportParameter2 {get;set;}
}

Above is the class that you want to instantiate and fill with parameters by string line. Below is XML that can do that:

<?xml version="1.0"?>
<Report1>
  <Orientation>Landscape</Orientation>
  <ReportParameter1>Page1</ReportParameter1>
  <ReportParameter2>Colorado</ReportParameter2>
</Report1>

To create an instance from the file use System.Xml.Serialization.XmlSerializer :

string xml = @"<?xml version=""1.0""?>
                <Report1>
                  <Orientation>Landscape</Orientation>
                  <ReportParameter1>Page1</ReportParameter1>
                  <ReportParameter2>Colorado</ReportParameter2>
                </Report1>";

            ///Create stream for serializer and put there our xml
            MemoryStream  str = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(xml));

            ///Getting type that we are expecting. We are doing it by passing proper namespace and class name string
            Type expectingType = Assembly.GetExecutingAssembly().GetType("ConsoleApplication1.Report1");

            XmlSerializer ser = new XmlSerializer(expectingType);

            ///Deserializing the xml into the object
            object obj = ser.Deserialize(str);

            ///Now we have our report instance initialized
            Report1 report = obj as Report1;

In this way you can prepare appropriate xml as string concatenation. That xml will contain all parameters for your report.

Then, you can convert it into the proper type.


Look at the Activator create instance method


All the classes will need to adhere to an interface. Then make an Generic Method which will be your eval and requires that interface. Here is an example of this (call the Usage static to see it in action):

public interface IOperation
{
    string OutputDirection { get; set; }   
};

public class MyOperation: IOperation
{
    public string OutputDirection { get; set; }
}   

public static class EvalExample
{

    public static T Eval<T>( string direction ) where T : IOperation
    {
            T target = (T) Activator.CreateInstance( typeof( T ) );

            target.OutputDirection = direction;

            return target; 
    }

    // Example only
    public static void Usage()
    {

        MyOperation mv = Eval<MyOperation>( "Horizontal" );

        Console.WriteLine( mv.OutputDirection ); // Horizontal
    }

}


Using the factory pattern, and reflection (as explained in this blog post), you would get:

static void Main(string[] args)
{
    ReportFactory<Report> factory = new ReportFactory<Report>();

    Report r1 = factory.CreateObject("LandscapeReport");
    Report r2 = factory.CreateObject("PortraitReport");

    Console.WriteLine(r1.WhoAmI());
    Console.WriteLine(r2.WhoAmI());
}

Which would output "Landscape" and "Portrait", respectivley.

Of course, for the plumbing, you need an interface that all your reports are based off of (which I assume you already have).

For this example:

public interface Report
{
    string WhoAmI();
}

And the two implemenations:

public class PortraitReport : Report
{
    public string WhoAmI()
    {
        return "Portrait";
    }

}

public class LandscapeReport : Report
{
    public string WhoAmI()
    {
        return "Landscape";
    }
}

The secret is in the ReportFactory, which uses Reflection to see what other classes are based on Report, and automatically register them for use, which I think is pretty cool:

public class ReportFactory<Report>
{
    private Dictionary<string, Type> reportMap = new Dictionary<string, Type>();

    public ReportFactory()
    {
        Type[] reportTypes = Assembly.GetAssembly(typeof(Report)).GetTypes();
        foreach (Type reportType in reportTypes)
        {
            if (!typeof(Report).IsAssignableFrom(reportType) || reportType == typeof(Report))
            {
                // reportType is not derived from Report
                continue; 
            }

            reportMap.Add(reportType.Name, reportType);
        }
    }

    public Report CreateObject(string ReportName, params object[] args)
    {
        return (Report)Activator.CreateInstance(reportMap[ReportName], args);
    }
}

So now all you have to do is just add any new implementations of Report in your assembly, and they will be available to the factory with no extra coding or changing other code files.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜