Форум программистов, компьютерный форум, киберфорум
C# Windows Forms
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.83/6: Рейтинг темы: голосов - 6, средняя оценка - 4.83
0 / 0 / 0
Регистрация: 05.10.2014
Сообщений: 67
1

Как преобразовать класс из динамически сгенерированного кода в локальный класс?

05.03.2016, 20:21. Показов 1223. Ответов 11
Метки нет (Все метки)

Исходные данные:
Форма, на ней кнопка, по нажатию на которую компилируется программа, совершает расчёты, возвращает результат.
Компилируется динамически через CodeDomProvider.CompileAssemblyFromSource
В качестве результата возвращает нужный класс посредством строчки return MyClass, которая прописана в конце вызываемого метода Main. MyClass содержит в себе переменные типа int, bool, списки.

При запуске строкой:
C#
1
var result = compilerResult.CompiledAssembly.GetType("MyNamespace.Test").GetMethod("Main").Invoke(null, null);
я получаю то что хотел в var result. Посредсвом студии вижу все значения.

Вопрос: как получить доступ к этим значениям (а не просто рассматривать их через студию)? Как преобразовать эту var result (являющуюся MyNamespace.MyClass в динамически сгенерированном коде) в локальный класс MyClass, который находится в запускающей программе.
__________________
Помощь в написании контрольных, курсовых и дипломных работ здесь
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
05.03.2016, 20:21
Ответы с готовыми решениями:

Как преобразовать класс?
Доброго времени суток, подскажите пожалуйста, как можно преобразовать анонимный список List<<...

Как преобразовать указатель на класс к указателю на асбтрактный класс?
Но тут есть одна тонкость- два промежуточных класса. Вот код, надеюсь всё из него понятно....

Как в функции my_function преобразовать ссылку на базовый класс в ссылку на класс B или С
Всем привет, как в функции my_function преобразовать ссылку на базовый класс в ссылку на класс B...

Есть класс A и класс B, класс B вложен в класс A и вложен в него, как классу B получить доступ к переменным класса A просто по имени?
На самом деле ничё фантастического я не прошу, ведь: template <class T> class matrix { ...

11
Эксперт .NETАвтор FAQ
10299 / 5033 / 1821
Регистрация: 11.01.2015
Сообщений: 6,224
Записей в блоге: 34
05.03.2016, 21:11 2
Цитата Сообщение от Andreys5 Посмотреть сообщение
в локальный класс MyClass, который находится в запускающей программе.
MyClass не может находится в запускающей программе никак. Для того что бы вы могли манипулировать классом как "обычным", компилятор еще на этапе компиляции должен знать об этом классе но он этого не знает, и не может знать, потому что вы генерируете свой MyClass динамичеки, в процессе работы программы.
Возможные варианты решения этой проблемы:
1) Интерфейсы. В вашей программе вы объявляете некий интерфейс, и ваш динамический класс MyClass должен реализовать этот интерфейс. Затем, получив ссылку на объект, вы его приводите к интерфейсу и дальше манипулируете как объектом, реализующим интерфейс. Это наиболее правильный способ. Фактически так работают плагины в приложениях.
2) Рефлексия. Вы можете получить доступ к любым полям, свойствам и методам, через объект типа Type. Получить его можно из вашего объекта: var type = obj.GetType(); Далее - можем получать свойства, поля, методы. Минусом данного подхода является неудобство использования (что может решиться написанием врапперов), медленная скорость работы, невозможность автоматического рефакторинга, поскольку методы и свойства задаются в виде строк.
3) Применение типа dynamic. По сути - тот же рефлекшен, сделанный в более удобном виде, но обладающий теми же недостатками.
0
0 / 0 / 0
Регистрация: 05.10.2014
Сообщений: 67
06.03.2016, 02:45  [ТС] 3
Что то не получается дружба с интерфейсами

В неймспейсы обоих программ (запускающей и динамически компилируемой) добавил:
C#
1
2
3
4
5
6
7
    interface Interface1
    {
        bool X1 { get; }
        int Y1 { get; }
...
        float Z1 { get; }
}
В MyClass обоих программ добавил
C#
1
2
3
4
5
6
        bool x;
        int y;
        float z;
        public bool X1{ get { return x; } }
        public int Y1 { get { return y; } }
        public float Z1 { get { return z; } }
Результат пытаюсь получить так:
C#
1
var result = (Interface1)compilerResult.CompiledAssembly.GetType("MyNamespace.Test").GetMethod("Main").Invoke(null, null);
При выполнении ругается: [System.InvalidCastException] = {"Не удалось привести тип объекта "MyNamespace.MyClass" к типу "MainProgs.Interface1"."}

Каким образом локальный MyClass заполнить данными из полученного MyClass? (без интерфейса я хотя бы через студию видел значения, а теперь сразу ошибка)
0
Эксперт .NET
5559 / 3611 / 1493
Регистрация: 09.05.2015
Сообщений: 8,666
06.03.2016, 02:57 4
А класс вы унаследовали от интерфейса то?
C#
1
2
3
4
class MyClass : Interface1
{
    ...
}
0
Эксперт .NETАвтор FAQ
10299 / 5033 / 1821
Регистрация: 11.01.2015
Сообщений: 6,224
Записей в блоге: 34
06.03.2016, 04:36 5
Цитата Сообщение от Andreys5 Посмотреть сообщение
В MyClass обоих программ добавил
Вы неправильно делаете. Если вы в двух разных проектах создали один и тот же класс, с одним и тем же именем, и в одном и том же неймспейсе, то все равно это разные классы. Ваш динамический код должен не создавать свой класс MyClass, а реально использовать класс MyClass из вашего текущего приложения. Для этого нужно добавить ссылку на вашу текущую dll (или exe) в references проекта, который вы компилируете.
Вот пример:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
 
namespace ConsoleApplication207
{
    class Program
    {
        static void Main(string[] args)
        {
            //исходный код
            string source = @"
              using System;
              using ConsoleApplication207;//<<---- используем наш неймспейс
 
              public static class Program
              {
                public static Foo Main()
                {
                  return new Foo{Field = 21};//<<---- создаем объект Foo
                }
              }";
 
            //настройки компиляции
            var compilerParams = new CompilerParameters { GenerateInMemory = true };
            //добавляем ссылку на себя, теперь наш код будет иметь доступ к классу Foo
            compilerParams.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
            //компиляция
            var results = new CSharpCodeProvider().CompileAssemblyFromSource(compilerParams, source);
            //обработка ошибок
            if (results.Errors.Count > 0)
                throw new Exception(results.Errors[0].ErrorText);
            //вызываем метод Main, получаем результат, приводим тип к Foo
            var res = (Foo)results.CompiledAssembly.GetType("Program").GetMethod("Main").Invoke(null, null);
 
            Console.WriteLine(res.Field);//вывод: 21
            Console.ReadLine();
        }
    }
 
    /// <summary>
    /// Класс
    /// </summary>
    public class Foo
    {
        public int Field;
    }
}
0
0 / 0 / 0
Регистрация: 05.10.2014
Сообщений: 67
06.03.2016, 05:03  [ТС] 6
Someone007, да, конечно унаследовал.

Storm23, спасибо. Такой вариант реального использования класса мне больше нравится.
Описанная вами ситуация напоминает мою, только наизнанку. У меня первоочередным является класс Foo в компилируемом приложении, и было бы здорово использовать класс Foo динамического приложения в вызывающем приложении (т.к. в вызывающем приложении я создал этот класс только потому что хотел достучаться к данным, которые в результате своей работы генерирует динамическое приложение).

Это реально?
0
Эксперт .NET
5559 / 3611 / 1493
Регистрация: 09.05.2015
Сообщений: 8,666
06.03.2016, 05:21 7
Вот пример с интерфейсом.
Вложения
Тип файла: zip DynamicAssemblyLoadingAndUsage.zip (5.8 Кб, 4 просмотров)
0
0 / 0 / 0
Регистрация: 05.10.2014
Сообщений: 67
06.03.2016, 19:56  [ТС] 8
Someone007, спасибо. Ваш пример отлично работает и показал где у меня была ошибка.
Но...
Мне кажется что интерфейсы подходят больше для задач где есть несколько (возможно десятки) возвращаемых значений. У меня несколько сотен уникальных значений в возвращаемом классе. Рост кода в обоих программах просто феноменальный, только ради того что бы из вызываемой программы забрать значения в вызывающую.
Я начинаю больше зацикливаться на том как бы мне что нибудь не упустить в том механизме который должен только передать значения - вместо того что бы думать о том как работать с этими значениями и что с ними делать дальше. Это явно не тот путь...
0
Эксперт .NETАвтор FAQ
10299 / 5033 / 1821
Регистрация: 11.01.2015
Сообщений: 6,224
Записей в блоге: 34
07.03.2016, 09:18 9
Andreys5,
У меня несколько сотен уникальных значений в возвращаемом классе.
Сотни полей в классе это не нормально. Может вам имеет смысл возвращать Dictionary<string, object> или DataTable ?
0
0 / 0 / 0
Регистрация: 05.10.2014
Сообщений: 67
07.03.2016, 11:30  [ТС] 10
Storm23, в вашем примере в посте 5 вы показали использование класса Foo основного приложения в компилируемом приложении. При этом тело класса Foo физически (первоначально) было в основном приложении (а не в основном).

Покажите, пожалуйста (если это возможно), пример кода, при условии что класс Foo физически (первоначально) находится в компилируемом приложении.
Т.е. string source выглядит так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
            string source = @"
              using System;
              using ConsoleApplication207;//<<---- используем наш неймспейс
 
              public static class Program
              {
                public static Foo Main()
                {
                  return new Foo{Field = 21};//<<---- создаем объект Foo
                }
 
                public class Foo
                {
                  public int Field;
                }
              }";
0
Эксперт .NETАвтор FAQ
10299 / 5033 / 1821
Регистрация: 11.01.2015
Сообщений: 6,224
Записей в блоге: 34
07.03.2016, 11:55 11
Лучший ответ Сообщение было отмечено Andreys5 как решение

Решение

Цитата Сообщение от Andreys5 Посмотреть сообщение
в вашем примере в посте 5 вы показали использование класса Foo основного приложения в компилируемом приложении. При этом тело класса Foo физически (первоначально) было в основном приложении (а не в основном).
Покажите, пожалуйста (если это возможно), пример кода, при условии что класс Foo физически (первоначально) находится в компилируемом приложении.
Так в том то и дело, что так сделать нельзя. Разница в том, что на этапе компиляции вашего исходного приложения, класса Foo еще не существует, и компилятор не может знать о нем. Поэтому вы не можете так просто использовать в вашем коде класс Foo.

Выходы из ситуации я вам уже описывал.
Ниже приведены примеры:
Через интерфейс
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
 
namespace ConsoleApplication207
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            //исходный код
            string source =
                @"
              using System;
              using ConsoleApplication207;//<<---- используем наш неймспейс
 
              public static class Program
              {
                public static Foo Create()
                {
                  return new Foo{Field = 21};//<<---- создаем объект Foo
                }
              }
 
              public class Foo : IFoo
              {
                public int Field{ get; set; }
              }";
 
            //настройки компиляции
            var compilerParams = new CompilerParameters {GenerateInMemory = true};
            //добавляем ссылку на себя, теперь наш код будет иметь доступ к классу Foo
            compilerParams.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
            //компиляция
            var results = new CSharpCodeProvider().CompileAssemblyFromSource(compilerParams, source);
            //обработка ошибок
            if (results.Errors.Count > 0)
                throw new Exception(results.Errors[0].ErrorText);
            //вызываем метод Create, получаем результат
            var res = (IFoo) results.CompiledAssembly.GetType("Program").GetMethod("Create").Invoke(null, null);
 
            Console.WriteLine(res.Field); //вывод: 21
            Console.ReadLine();
        }
    }
 
    public interface IFoo
    {
        int Field { get; }
    }
}

Через dynamic
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
 
namespace ConsoleApplication207
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            //исходный код
            string source =
                @"
              using System;
              using ConsoleApplication207;//<<---- используем наш неймспейс
 
              public static class Program
              {
                public static Foo Create()
                {
                  return new Foo{Field = 21};//<<---- создаем объект Foo
                }
              }
 
              public class Foo
              {
                public int Field;
              }";
 
            //настройки компиляции
            var compilerParams = new CompilerParameters {GenerateInMemory = true};
            //добавляем ссылку на себя, теперь наш код будет иметь доступ к классу Foo
            compilerParams.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
            //компиляция
            var results = new CSharpCodeProvider().CompileAssemblyFromSource(compilerParams, source);
            //обработка ошибок
            if (results.Errors.Count > 0)
                throw new Exception(results.Errors[0].ErrorText);
            //вызываем метод Create, получаем результат
            var res = (dynamic) results.CompiledAssembly.GetType("Program").GetMethod("Create").Invoke(null, null);
 
            Console.WriteLine(res.Field); //вывод: 21
            Console.ReadLine();
        }
    }
}

Через Dictionary<string, object>
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.CSharp;
 
namespace ConsoleApplication207
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            //исходный код
            string source =
              @"
              using System;
              using System.Collections.Generic;
 
              public static class Program
              {
                public static Dictionary<string, object> Create()
                {
                  var res = new Dictionary<string, object>();
                  res[""Field""] = 21;
 
                  return res;
                }
              }";
 
            //настройки компиляции
            var compilerParams = new CompilerParameters {GenerateInMemory = true};
            //добавляем ссылку на себя, теперь наш код будет иметь доступ к классу Foo
            compilerParams.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
            //компиляция
            var results = new CSharpCodeProvider().CompileAssemblyFromSource(compilerParams, source);
            //обработка ошибок
            if (results.Errors.Count > 0)
                throw new Exception(results.Errors[0].ErrorText);
            //вызываем метод Create, получаем результат
            var res = (Dictionary<string, object>)results.CompiledAssembly.GetType("Program").GetMethod("Create").Invoke(null, null);
 
            Console.WriteLine(res["Field"]); //вывод: 21
            Console.ReadLine();
        }
    }
}
2
0 / 0 / 0
Регистрация: 05.10.2014
Сообщений: 67
08.03.2016, 01:09  [ТС] 12
Storm23, спасибо большое.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
08.03.2016, 01:09

Как добавлять класс динамически
Люди, возникла проблема в следующем ,если кто знает подскажите плиз. Допустим есть некий объект А...

Как преобразовать этот класс из Delphi в c++
TKarta=class(TObject) private FDvig: boolean; Fi: integer; Fj: integer; ...

Как преобразовать массив в класс и обратно?
Как преобразовать массив в класс и обратно?и как вызвать это в мэйне? есть класс class Array...

Как преобразовать объект в класс из разных источников?
Здравствуйте, подскажите как преобразовать объект в класс из разных источников. У меня есть класс,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
12
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru