2010-04-08 3 views
4

Вот упрощенная версия того, что я пытаюсь сделать:C#: динамически создавать разные классы в одном выражении?

Не имея несколько положений if..else и переключение блоков, можно имитировать поведение Eval в JavaScript() содрогания для создания экземпляра класса в 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() 

Необходимость проистекает из того факта, что у меня есть 6 типов Crystal Reports (которые делают то же самое, но выглядит сильно отличается) для 50 государств. Каждый из них состоит из 3 стилей, вместо того, чтобы принимать понятие гигантского блока переключателей с вложенными if..else заявлениями, определяющими, какие из 900 отчетов использовать, я надеялся на подобное eval-решение.

ответ

3

Вы можете использовать Activator.CreateInstance("myAssembly", "PortrainReport");. Хотя более читаемым способом было бы создать портретную фабрику, которая создала бы правильный тип для вас.

+0

Возможно, я не понимаю, как это работает, поэтому позвольте мне добавить это, чтобы увидеть, изменит ли он ваш ответ: Эта же строка также может развернуться и оценить значение «LandscapeReport()» на основе значения строка reportType. Ваша идея по-прежнему работает с одной строкой кода? –

+0

А теперь я вижу, что вы, ребята, говорите об одном и том же. Я собираюсь проверить это и отправить обратно. –

+0

У меня все еще немного неприятностей с Активатором ... Я начинаю думать, что Активатор не хочет создавать экземпляр объекта отчета CrystalReports таким образом, поскольку программа бросает «TypeLoadException» был необработанным «ошибка во время выполнения в строке, где я пытаюсь создать экземпляр отчета. Думаю, мне, возможно, придется посмотреть на возможность Фабрики. –

1

Посмотрите на Activator create instance method

+0

Это предложение выглядит немного ближе к тому, что я надеялся найти ... Имеет ли значение, что класс является файлом .rpt? (Это всего лишь класс, который наследует CrystalDecisions.CrystalReports.Engine.ReportClass) –

1

Все классы должны придерживаться интерфейса. Затем создайте общий метод, который будет вашим eval и потребует этого интерфейса. Вот пример этого (вызов Использование статического, чтобы увидеть его в действии):

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 
    } 

} 
+0

Я, вероятно, должен был указать, что OutputDirection в моем примере не является переменной или частью класса, но на самом деле заканчивает определение того, какой из них (2) должны использоваться отчеты. –

1

Используя шаблон фабрики и отражения (как объяснено в этом blog po ст), вы получите:

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

Который выдавал бы «Пейзаж» и «Портрет», уважителей.

Конечно, для сантехники вам нужен интерфейс, на котором основаны все ваши отчеты (что, как я полагаю, у вас уже есть).

Для этого примера:

public interface Report 
{ 
    string WhoAmI(); 
} 

И два implemenations:

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

} 

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

Секрет в ReportFactory, который использует отражение, чтобы увидеть, что другие классы основаны на докладе, и автоматически регистрировать они для использования, которые я считаю довольно крутыми:

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

Итак, теперь вы все нужно просто добавить любые новые реализации отчета в вашей сборке, и они будут доступны на заводе без дополнительного кодирования или изменения других файлов кода.

+0

Ты моя последняя надежда, я собираюсь попробовать это и отчитаться! Спасибо всем, ваши ответы были быстрыми и разнообразными (что полезно!). –

+0

На этот раз мне не нужно было получать эту фантазию, но это выглядело как элегантное решение. –

2

Как указано выше, вы можете использовать класс Activator для создания экземпляра класса по его текстовому имени.

Но есть еще один вариант. Когда вы сказали об использовании функции eval like в C#, которую я предположил, вы не только хотите создать экземпляр класса по его текстовому имени, но и заполнить его свойствами из той же строки.

Для этого вам необходимо использовать десерилизацию.

Deserialization преобразует строку как представление класса в свой экземпляр и восстанавливает все его свойства, которые были указаны в строке.

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 будет содержать все параметры для вашего отчета.

Затем вы можете преобразовать его в соответствующий тип.

+0

Кажется, что это может сработать, но это все равно заканчивается тем, что я оставляю одну из моих первоначальных проблем - то есть (в вашей последней строке кода) вы предполагаете, что мой объект будет всего лишь 1, так как я бы приходится учитывать несколько сотен различных типов. –

+0

Ваш пример дал мне то, что мне нужно, чтобы правильно реализовать одно из перечисленных выше решений - метод Activator.CreateInstance (Type t). Ваша строка создания экземпляра expectingType очень помогла - Спасибо! –

Смежные вопросы