Подтвердить что ты не робот

Передача общего класса <TObject> в форму

Я не могу найти ответ на этот вопрос путем поиска, поэтому здесь идет....

Я знаю, что я могу передавать объекты класса в общих чертах другим классам, используя этот тип кода:

public class ClsGeneric<TObject> where TObject : class
{
    public TObject GenericType { get; set; }
}

Тогда построим таким образом:

ClsGeneric<MyType> someName = new ClsGeneric<MyType>()

Однако у меня есть приложение, которое требует от меня открыть форму и каким-то образом перейти в общий тип для использования в этой форме. Я пытаюсь повторно использовать эту форму для разных типов классов.

Кто-нибудь знает, если это возможно, и если да, то как?

Я немного экспериментировал с конструктором формы, но безрезультатно.

Большое спасибо заранее, Dave

ОБНОВЛЕНО: Разъяснение того, к какому результату я пытаюсь достичь,

введите описание изображения здесь

ОБНОВЛЕНО: 4-й AUG, я немного продвинулся дальше, но я предлагаю щедрость для решения. Вот что у меня есть сейчас:

interface IFormInterface
{
    DialogResult ShowDialog();
}


public class FormInterface<TObject> : SubForm, IFormInterface where TObject : class
{ }

public partial class Form1 : Form
{
    private FormController<Parent> _formController;

    public Form1()
    {
        InitializeComponent();
            _formController = new FormController<Parent>(this.btnShowSubForm, new DataController<Parent>(new MeContext()));   
    }
}

public class FormController<TObject> where TObject : class
{
    private DataController<TObject> _dataController;
    public FormController(Button btn, DataController<TObject> dataController)
    {
        _dataController = dataController;
        btn.Click += new EventHandler(btnClick);
    }

    private void btnClick(object sender, EventArgs e)
    {
        showSubForm("Something");
    }

    public void showSubForm(string className)
    {
        //I'm still stuck here because I have to tell the interface the Name of the Class "Child", I want to pass <TObject> here.
        // Want to pass in the true Class name to FormController from the MainForm only, and from then on, it generic.

        IFormInterface f2 = new FormInterface<Child>();
        f2.ShowDialog();
    }
}

class MeContext : DbContext
{
    public MeContext() : base(@"data source=HAZEL-PC\HAZEL_SQL;initial catalog=MCL;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework") { }
    public DbSet<Parent> Child { get; set; }
}

public class DataController<TObject> where TObject : class
{
    protected DbContext _context;

    public DataController(DbContext context)
    {
        _context = context;
    }
}

public class Parent
{
    string Name { get; set; }
    bool HasChildren { get; set; }
    int Age { get; set; }
}

public class Child
{
    string Name { get; set; }
    int Age { get; set; }
}
4b9b3361

Ответ 1

Я думаю, вы можете добавить аргумент нового типа в FormController:

public class FormController<TParent, TChild>
  where TParent : class
  where TChild : class 
{
    ...

    public void showSubForm(string className)
    {
        IFormInterface f2 = new FormInterface<TChild>();
        f2.ShowDialog();
    }
}

Ответ 2

Возможно, вы пробовали это, но можете создать собственный класс:

public class GenericForm<TObject> : Form where TObject : class
{
    // Here you can do whatever you want,
    // exactly like the example code in the
    // first lines of your question
    public TObject GenericType { get; set; }

    public GenericForm()
    {
        // To show that this actually works,
        // I'll handle the Paint event, because
        // it is executed AFTER the window is shown.
        Paint += GenericForm_Paint;
    }

    private void GenericForm_Paint(object sender, EventArgs e)
    {
        // Let print the type of TObject to see if it worked:
        MessageBox.Show(typeof(TObject).ToString());
    }
}

Если вы создадите экземпляр этого типа:

var form = new GenericForm<string>();
form.Show();

Результат:

введите описание изображения здесь

Далее, вы можете создать экземпляр типа TObject из класса GenericForm, используя класс Activator:

GenericType = (TObject)Activator.CreateInstance(typeof(TObject));

В этом примере, поскольку мы знаем, что это строка, мы также знаем, что она должна генерировать исключение, потому что строка не имеет конструктора без параметров. Итак, вместо этого используйте конструктор char array (char[]):

GenericType = (TObject)Activator.
         CreateInstance(typeof(TObject), new char[] { 'T', 'e', 's', 't' });

MessageBox.Show(GenericType as string);

Результат:

введите описание изображения здесь

Тогда сделайте домашнее задание. Следующий код должен достичь того, что вы хотите сделать.

public class Parent
{
    string Name { get; set; }
    bool HasChildren { get; set; }
    int Age { get; set; }
}

public class Child
{
    string Name { get; set; }
    int Age { get; set; }
}

public class DataController<TObject> where TObject : class
{
    protected DbContext _context;

    public DataController(DbContext context)
    {
        _context = context;
    }
}

public class FormController<TObject> where TObject : class
{
    private DataController<TObject> _dataController;

    public FormController(Button btn, DataController<TObject> dataController)
    {
        _dataController = dataController;
        btn.Click += new EventHandler(btnClick);
    }

    private void btnClick(object sender, EventArgs e)
    {
        GenericForm<TObject> form = new GenericForm<TObject>();
        form.ShowDialog();
    }
}

public class GenericForm<TObject> : Form where TObject : class
{
    public TObject GenericType { get; set; }

    public GenericForm()
    {
        Paint += GenericForm_Paint;
    }

    private void GenericForm_Paint(object sender, EventArgs e)
    {
        MessageBox.Show(typeof(TObject).ToString());

        // If you want to instantiate:
        GenericType = (TObject)Activator.CreateInstance(typeof(TObject));
    }
}

Однако, глядя на ваш текущий пример, у вас есть два класса: Parent и Child. Если я правильно понимаю, это единственные возможности быть типом TObject.

Если это так, то приведенный выше код взорвется, если кто-то передаст string в качестве параметра типа (когда выполнение достигнет Activator.CreateInstance) - с исключением во время выполнения (поскольку string не имеет без параметров конструктор):

введите описание изображения здесь

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

введите описание изображения здесь

Код выглядит следующим образом.

// Maybe you should give a better name to this...
public interface IAllowedParamType { }

// Inherit all the possible classes with that
public class Parent : IAllowedParamType
{
    string Name { get; set; }
    bool HasChildren { get; set; }
    int Age { get; set; }
}

public class Child : IAllowedParamType
{
    string Name { get; set; }
    int Age { get; set; }
}

// Filter the interface on the 'where'
public class DataController<TObject> where TObject : class, IAllowedParamType
{
    protected DbContext _context;

    public DataController(DbContext context)
    {
        _context = context;
    }
}

public class FormController<TObject> where TObject : class, IAllowedParamType
{
    private DataController<TObject> _dataController;

    public FormController(Button btn, DataController<TObject> dataController)
    {
        _dataController = dataController;
        btn.Click += new EventHandler(btnClick);
    }

    private void btnClick(object sender, EventArgs e)
    {
        GenericForm<TObject> form = new GenericForm<TObject>();
        form.ShowDialog();
    }
}

public class GenericForm<TObject> : Form where TObject : class, IAllowedParamType
{
    public TObject GenericType { get; set; }

    public GenericForm()
    {
        Paint += GenericForm_Paint;
    }

    private void GenericForm_Paint(object sender, EventArgs e)
    {
        MessageBox.Show(typeof(TObject).ToString());

        // If you want to instantiate:
        GenericType = (TObject)Activator.CreateInstance(typeof(TObject));
    }
}

UPDATE

Как отметил RupertMorrish, вы все равно можете скомпилировать следующий код:

public class MyObj : IAllowedParamType
{
    public int Id { get; set; }

    public MyObj(int id)
    {
        Id = id;
    }
}

И это должно по-прежнему создавать исключение, потому что вы просто удалили неявный конструктор без параметров. Конечно, если вы знаете, что делаете, это трудно осуществить, однако мы можем запретить это, используя new() в фильтрации типа "where", а также избавиться от материала Activator.CreateInstance.

Весь код:

// Maybe you should give a better name to this...
public interface IAllowedParamType { }

// Inherit all the possible classes with that
public class Parent : IAllowedParamType
{
    string Name { get; set; }
    bool HasChildren { get; set; }
    int Age { get; set; }
}

public class Child : IAllowedParamType
{
    string Name { get; set; }
    int Age { get; set; }
}

// Filter the interface on the 'where'
public class DataController<TObject> where TObject : new(), IAllowedParamType
{
    protected DbContext _context;

    public DataController(DbContext context)
    {
        _context = context;
    }
}

public class FormController<TObject> where TObject : new(), IAllowedParamType
{
    private DataController<TObject> _dataController;

    public FormController(Button btn, DataController<TObject> dataController)
    {
        _dataController = dataController;
        btn.Click += new EventHandler(btnClick);
    }

    private void btnClick(object sender, EventArgs e)
    {
        GenericForm<TObject> form = new GenericForm<TObject>();
        form.ShowDialog();
    }
}

public class GenericForm<TObject> : Form where TObject : new(), IAllowedParamType
{
    public TObject GenericType { get; set; }

    public GenericForm()
    {
        Paint += GenericForm_Paint;
    }

    private void GenericForm_Paint(object sender, EventArgs e)
    {
        MessageBox.Show(typeof(TObject).ToString());

        // If you want to instantiate:
        GenericType = new TObject();
    }
}

Ответ 3

Итак, как я понимаю, вы хотите, чтобы Form<T> открывался при некоторых действиях в MainForm, с вашим MainForm с помощью FormController, в качестве менеджера всех ваших форм, ретрансляции общей информации типа на ваш Form<T>. Кроме того, созданный объект вашего класса Form<T> должен запросить экземпляр класса DatabaseController<T> из вашего FormController.

Если это так, может быть предпринята следующая попытка:

MainForm получает ссылку на экземпляр FormController при инициализации конструктора или имеет другой способ взаимодействия с FormController, например. a CommonService, из которых оба знают и т.д.

Это позволяет MainForm вызывать общий метод FormController для создания и отображения нового объекта Form:

void FormController.CreateForm<T> () 
{
    Form<T> form = new Form<T>();
    form.Show();
    // Set potential Controller states if not stateless
    // Register forms, etc.
}

с Form<T> по строкам:

class Form<T> : Form where T : class
{
    DatabaseController<T> _dbController;
    Form(FormController formController)
    {
        _dbController = formController.CreateDatabaseController<T>();
    }
}

Теперь у вас есть несколько способов для формы получить экземпляр DatabaseController:

1. Ваш Form<T> получает ссылку FormController или имеет другой способ связи с ним для вызова метода по строкам:
DatabaseController<T> FormController.CreateDatabaseController<T> () 
{
    return new DatabaseController<T>();
}

Ваш FormController не обязательно должен быть общим, иначе вам понадобится новый экземпляр FormController для каждого T. Просто нужно предоставить общий метод.

  1. Ваш Form<T> получает экземпляр DatabaseController из FormController при инициализации конструктора:

    void FormController.CreateForm() {   Форма формы = новая форма (новый DatabaseController());   form.Show(); }

с Form<T>:

class Form<T> : Form where T : class
{
    DatabaseController<T> _dbController;
    Form(DatabaseController<T> controller) 
    {
         _dbController = controller;
    }
}

3. Как и в случае с 2, но ваши Form<T> и DatabaseController<T> предоставляют статические методы FactoryMethods, чтобы оставаться верными первому Одиночной ответственности. например:.
public class Form<T> : Form where T : class
{
    private DatabaseController<T> _dbController;

    public static Form<T> Create<T>(DatabaseController<T> controller)
    {
        return new Form<T>(controller);
    }

    private Form(DatabaseController<T> controller) 
    {
         _dbController = controller;
    }
}

4. Вы также можете использовать IoC Container для регистрации и получения экземпляров определенного типа во время выполнения. Каждый Form<T> получает экземпляр контейнера IoC во время выполнения и запрашивает соответствующий DatabaseController<T>. Это позволяет лучше управлять временем жизни вашего контроллера и формировать объекты в приложении.

Ответ 4

Ну, я не собираюсь вдаваться в подробности здесь и хватит только на некоторые чертежи. В этом случае я бы использовал комбинацию инъекции конструктора Unity с общим factory для обработки экземпляра, заданного типом в основной форме.

Это не так сложно, взгляните на документацию Unity на Инъекция зависимостей с единством

Причина выбора Unity из всех контейнеров DI заключается в том, что она была частью Enterprise Library от самой Microsoft и теперь продолжает жить как независимая библиотека в виде Nugget. мой знакомый недавно поместил Unity в ядро ​​.Net. Проще говоря, он подает самый сложный контейнер.

Что касается factory, я считаю это необходимым, потому что вы не хотите создавать конкретный поиск для обработки всех возможных типов, поэтому он явно должен быть общим factory. Я бы посоветовал сделать ваш factory синглтон и поместить его в целый другой проект, тем самым отделив ваш проект пользовательского интерфейса от моделей, и обе стороны будут общаться через этот мост DI. вы можете сделать еще один шаг и обработать ваши модели, используя отражение сборки. извините за слишком общее, но я действительно не знаю, насколько вы знакомы с этими шаблонами. Это действительно стоит потратить некоторое время и использовать эти шаблоны. по моему скромному мнению, от этих маневров не избежать, если вы хотите действительно масштабируемое программное обеспечение.

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

Ответ 5

Попробуйте метод Factory.

public interface IProvider
{
    T GetObject<T>();
}

Форма верхнего уровня:

public class TopLevelForm : Form
{
    public TopLevelForm(IProvider provider):base()
    {
         _provider = provider;
    }

    private void ShowSecondForm()
    {
        var f2 = new SecondForm(provider);
        f2.Show();
    }
}

Форма второго уровня:

public class SecondLevelForm : Form
{
    public SecondLevelForm(IProvider provider):base()
    {
         _data = provider.GetObject<MyEntity>();
    }
}

Что касается реализации IProvider - существует множество методов, начиная с самого простого, return new T();