C#:在同一条语句中动态实例化不同的类?

发布于 2024-08-28 22:26:55 字数 555 浏览 4 评论 0原文

这是我正在尝试做的事情的简化版本:

在没有多个 if..else 子句和 switch 块的情况下,我可以模仿 Javascript 的 eval() 的行为来实例化 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()

这一需求源于这样一个事实:我有 50 个州的 6 种类型的 Crystal Reports(它们做同样的事情,但看起来截然不同)。每种样式都有 3 种样式,我希望有一个类似 eval 的解决方案,而不是考虑使用嵌套 if..else 语句确定要使用 900 个报告中的哪一个的巨大 switch 块的概念。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

兮子 2024-09-04 22:26:55

您可以使用 Activator.CreateInstance(" myAssembly", "PortrainReport");。尽管更易读的方法是创建一个肖像工厂,这将为您创建正确的类型。

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.

冷情 2024-09-04 22:26:55

正如上面提到的,您可以使用 Activator 类通过其文本名称创建该类的实例。

但是,还有一种选择。
当您讲述在 C# 中使用 eval 之类的函数时,我假设您不仅想通过其文本名称创建类的实例,而且还要使用同一字符串中的属性填充它。

为此,您需要使用反序列化。

反序列化将类的字符串表示形式转换为其实例,并恢复字符串中指定的所有属性。

XML 序列化。它使用 XML 文件来转换为实例。
这是一个小例子:

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

上面是您要实例化并通过字符串行填充参数的类。
下面是可以执行此操作的 XML:

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

要从文件创建实例,请使用 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;

通过这种方式,您可以准备适当的 xml 作为字符串连接。该 xml 将包含您的报告的所有参数。

然后,您可以将其转换为正确的类型。

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.

女皇必胜 2024-09-04 22:26:55

所有类都需要遵守一个接口。然后创建一个通用方法,它将作为您的评估并需要该接口。下面是一个示例(调用静态用法以查看其实际情况):

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
    }

}

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
    }

}
夏九 2024-09-04 22:26:55

使用工厂模式和反射(如 博客文章st),你会得到:

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());
}

分别输出“Landscape”和“Portrait”。

当然,对于管道,您需要一个所有报告都基于的界面(我假设您已经拥有)。

对于这个例子:

public interface Report
{
    string WhoAmI();
}

以及两个实现:

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

}

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

秘密在于 ReportFactory,它使用 Reflection 来查看其他类是基于 Report 的,并自动注册它们以供使用,我认为这非常酷:

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);
    }
}

所以现在您所要做的就是只需在程序集中添加 Report 的任何新实现,工厂即可使用它们,无需额外编码或更改其他代码文件。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文