10

我似乎无法通过搜索找到答案,所以这里......

我知道我可以通过使用这种类型的代码将 Class 对象一般地传递​​给其他类:

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

然后以这种方式构建:

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

但是,我有一个应用程序需要我打开一个表单并以某种方式传入泛型类型以在该表单中使用。我试图能够将此表单重新用于许多不同的类类型。

有谁知道这是否可能,如果可以,怎么做?

我已经对 Form 构造函数进行了一些实验,但无济于事。

非常感谢,戴夫

更新:澄清我想要达到的结果是

在此处输入图像描述

更新:8 月 4 日,我已经前进了一点,但我为解决方案提供了赏金。这是我现在拥有的:

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's 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; }
}
4

5 回答 5

11

也许你已经尝试过了,但你可以创建一个自定义类:

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's print the type of TObject to see if it worked:
        MessageBox.Show(typeof(TObject).ToString());
    }
}

如果您像这样创建它的实例:

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

结果是:

在此处输入图像描述

更进一步,您可以使用类TObjectGenericForm类中创建类型的实例Activator

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

在这个例子中,既然我们知道那是一个字符串,我们也知道它应该抛出一个异常,因为字符串没有无参数的构造函数。因此,让我们改用 char 数组 ( 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.

如果是这种情况,那么如果有人将 astring作为类型参数传递(当执行到达 时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));
    }
}

更新

正如 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();
    }
}
于 2017-08-04T19:45:48.473 回答
3

我认为你可以添加一个新的类型参数FormController

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

    public void showSubForm(string className)
    {
        IFormInterface f2 = new FormInterface<TChild>();
        f2.ShowDialog();
    }
}
于 2017-08-05T08:34:39.580 回答
2

因此,据我了解,您希望 aForm<T>打开 中的某些操作MainForm,并MainForm使用 aFormController作为所有表单的管理器,将通用类型信息传递给您的Form<T>. 此外,您的Form<T>类的实例化对象应该DatabaseController<T>从您的FormController.

如果是这种情况,以下尝试可能会奏效:

MainForm在构造函数初始化时接收对FormController实例的引用或具有与 交互的另一种方式FormController,例如CommonService两者都知道的 a 等。

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

现在您有几种方法可以让 Form 接收 DatabaseController 实例:

1. 您Form<T>收到了 的引用FormController或有另一种与之通信的方式来调用方法,如下所示:

DatabaseController<T> FormController.CreateDatabaseController<T> () 
{
    return new DatabaseController<T>();
}

FormController不需要是通用的,否则您需要为每个 T 提供一个新的 FormController 实例。它只需要提供一个通用方法。

  1. Form<T>在构造函数初始化时从 FormController 接收 DatabaseController 的实例:

    void FormController.CreateForm () { Form form = new Form(new 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>. 这使您可以更好地管理控制器的生命周期并在应用程序中形成对象。

于 2017-08-07T10:09:00.717 回答
1

好吧,我不打算在这里详细介绍,仅对一些蓝图就足够了。在这种情况下,我将结合使用 Unity 构造函数注入和通用工厂来处理给定主表单类型的实例化。

没有那么复杂,请查看 Unity 的 Dependency Injection with Unity文档

从所有 DI 容器中选择 Unity 的原因是它是 Microsoft 本身企业库的一部分,现在继续以 Nugget 形式作为独立库继续存在。我的一个朋友最近也将 Unity 移植到了 .Net 核心。简而言之,它是现有最精致的容器。

至于工厂,我认为这是必要的,因为您不想创建一个具体的查找来处理所有可能的类型,所以它显然必须是一个通用工厂。我建议您将您的工厂设为单例并将其放在另一个项目中,从而将您的 UI 项目与模型分开,双方将通过这个 DI 桥进行通信。您甚至可以更进一步,使用程序集反射处理您的模型类型。抱歉太笼统了,但我真的不知道你对这些模式有多熟悉。真的值得花一些时间来利用这些模式。在我看来,如果您想要一个真正可扩展的软件,就无法摆脱这些操作。

如果您正在寻找有关实施上述任何策略的提示,您可以私下联系我。

于 2017-08-11T04:19:11.850 回答
0

试试工厂方法。

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

于 2017-08-02T14:13:14.110 回答