以下是我正在尝试做的简化版本:
如果没有多个if..else子句和switch块,我可以模仿Javascript的eval() shudder 在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种样式,而不是通过嵌套if..else语句来确定巨型开关块的概念,确定要使用的900个报告中的哪一个,我希望得到类似eval的解决方案。
答案 0 :(得分:3)
您可以使用Activator.CreateInstance("myAssembly", "PortrainReport");
。虽然更可读的方法是创建一个Portrait Factory,它会为你创建正确的类型。
答案 1 :(得分:2)
如上所述,您可以使用Activator类按文本名称创建类的实例。
但是,还有一个选择。 当你在c#i中假设使用eval like函数时,你不仅要通过文本名创建类的实例,还要用同一个字符串中的属性填充它。
为此,您需要使用反序列化。
反序列化将类的字符串表示转换为其实例并恢复其在字符串中指定的所有属性。
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将包含报告的所有参数。
然后,您可以将其转换为正确的类型。
答案 2 :(得分:1)
答案 3 :(得分:1)
所有类都需要遵循一个接口。然后创建一个通用方法,它将是您的eval并需要该接口。以下是此示例(调用Usage static以查看其在行动中):
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
}
}
答案 4 :(得分:1)
使用工厂模式和反射(如本blog po 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实现,它们将在出厂时可用,无需额外编码或更改其他代码文件。