C #: dynamically create different classes in one expression?

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

Without a few if..else arguments and lock blocks, can I mimic the behavior of javascript 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 (which do the same, but look radically different) for 50 states. There are three styles, and not the concept of a giant block of switches with nested if..else statements that determine which of the 900 reports to use, I was hoping for a similar eval solution.

+4
source share
5 answers

You can use Activator.CreateInstance("myAssembly", "PortrainReport"); . Although a more readable way would be to create a portrait of Factory, which would create the right type for you.

+3
source

As stated above, you can use the Activator class to instantiate the class by its text name.

But there is another option. When you talked about using a similar eval function in C #, I assumed that you would not only want to instantiate the class by its text name, but also populate it with properties from the same line.

For this you need to use deserialization.

Deserialization converts the string as a representation of the class into its instance and restores all its properties that were specified in the string.

Xml serialization. Its using an XML file to convert to an instance. Here is a small example:

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

Above is the class you want to create and populate with parameters with a string. Below is the XML that can do this:

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

To create an instance from a 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; 

This way you can prepare the corresponding xml as a string concatenation. This xml will contain all the parameters for your report.

Then you can convert it to the appropriate type.

+2
source

All classes will have to adhere to the interface. Then create a generic method that will be your eval and will require this interface. Here is an example of this (call static 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 } } 
+1
source

Using the factory pattern and reflection (as explained in this blog po st), you will 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()); } 

What would bring out "Landscape" and "Portrait", respectfully.

Of course, for plumbing, you need an interface on which all your reports are based (which, I believe, you already have).

In this example:

 public interface Report { string WhoAmI(); } 

And two possibilities:

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

A secret in ReportFactory that uses Reflection to see which 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 Report implementations in your assembly and they will be available to the factory without additional coding or changing other code files.

+1
source

Source: https://habr.com/ru/post/1306410/


All Articles