【问题标题】:Generic class constraint where <T> is a type constraining the generic class泛型类约束,其中 <T> 是约束泛型类的类型
【发布时间】:2017-01-21 18:06:41
【问题描述】:

也许不是最准确的标题,但描述起来有点困难;也许你们可以在这里帮助我?我正在使用 MVC 格式编写游戏,并且我希望每个基类(控制器、模型和视图)都有对其随附功能的引用,形成一种三角形(即模型具有对控制器的引用定义它,以及引用它的视图等)这些类中的大部分看起来像这样:

public class Model {
  public Controller controller;
  public View view;

  public void Connect (Controller controller, View view) {
    this.controller = controller;
    this.view = view; 
  }
}

这没关系,但每当我打算调出 ChildModel 的控制器时,我都需要强制转换为适当的 ChildController 以获得正确的版本。我可以创建一个实用方法/getter 来获取适当的强制转换项,但我不想为每个子类重写这段代码。我以为我可以通过使基类通用来解决这个问题,但现在我遇到了一个问题,即新的通用类需要引用试图定义它们的类,因此:

public class Model<V, C> where V : View<?, C> where C : Controller<?, V> {
  public Controller<?, V> controller;
  public View<?, C> view;

  public void Connect (Controller<?, V> controller, View<?, C> view) {
    this.controller = controller;
    this.view = view; 
  }
}

如您所见,这很快在基类中变得一团糟。我不知道为试图定义约束的模型放置什么符号(参考上面的示例)。将“模型”放入问号似乎也无法编译,因为我遇到了地狱般的拳击转换问题。

有没有办法完成我所追求的,或者我只是想在这里太聪明了?如果这可行,我希望能够声明类型限制为“三角形”的子类,因此我可以避免不必要的强制转换或辅助方法:

public class ChildModel<ChildView, ChildController> {

  public ChildModel () {
     this.controller <- calls ChildController type, not base type!
  }
}

有人有什么想法吗?

【问题讨论】:

标签: c# generics model-view-controller unity3d


【解决方案1】:

您似乎将所有权与交互混淆了。所有权意味着一个人拥有另一个人,而交互意味着他们如何相互交流。 MVC 主要定义了三个参与者之间的交互,尽管您可以说视图和控制器都拥有一个模型。



在您显示的代码中,一个类拥有一个属性,因此控制器类拥有一个视图,而一个视图拥有一个控制器。

var model = new Model();
var view  = new View<Controller<Model, View<Controller, Model>, ...

这不适用于您希望的泛型,因为交互变得循环。这是鸡和蛋的问题:鸡来自鸡产下的鸡蛋。我们可以通过赋予控制器视图所有权以及模型的控制器和视图所有权来解决大部分问题。

public class Model
{   
}

public interface IView<M>
{
    M Model { get; }
}

public class MyView : IView<Model>
{
    public MyView(Model model)
    {
        Model = model;
    }

    public Model Model
    {
        get;
    }
}

public interface IController<V, M>
{
    M Model { get; }
    V View { get; }
}

public class MyController : IController<MyView, Model>
{
    public MyController(MyView view, Model model)
    {
        View = view;
        Model = model;
    }

    public Model Model
    {
        get;
    }

    public MyView View
    {
        get;
    }
}

我们仍然使用泛型来执行此操作,并且您可以轻松访问迄今为止的大部分信息,而无需引入循环引用。

class Program
{
    public static void Main()
    {
        var model      = new Model();
        var view       = new MyView(model);
        var controller = new MyController(view, model);
    }
}

现在,如果您想确保视图具有对控制器的引用,您可以通过属性来做到这一点。

view.Controller = controller;

您可以忽略我刚刚向您展示的所有内容 - 并采用属性注入路线。这意味着您可以简单地执行此操作,而不是通过构造函数获取依赖项,这会对如何创建对象产生循环引用限制。

var model = new Model();
var view  = new View();
var controller = new Controller();

model.View = view;
model.Controller = controller;

view.Controller = controller;
view.Model = model;

controller.View = view;
controller.Model = model;

无论您使用什么方法,诀窍都是避免当前代码中存在的循环依赖问题。大多数 MVC 框架提供丰富的数据绑定,打破了类之间的直接耦合,但如果你没有,你必须要么编写一些东西,要么找到一些东西,或者在语言规则的范围内工作。

有很多方法可以解决这个问题。在我写这篇文章时,发布了另一个答案,所以你也应该看看。

【讨论】:

  • 附带问题:您使用哪个程序或工具绘制该图?
  • 非常感谢!我喜欢这个实现,我想我会继续前进。谢谢人:)
  • 我很高兴它有帮助。设计、开发、分享!
【解决方案2】:

这是我的建议。 1. 你应该使用 Controller 作为 MVC 模式的主要部分。控制器应该从模式中获取信息,对其进行处理,然后调用视图。

这是我的 Controller

基类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Inheritance.Classes
{
    public class Controller<T, U> where T : Model, new() where U : View, new()
    {
        protected T _model;
        protected U _view;

        public Controller()
        {
            this._model = new T();
            this._view = new U();
        }

        public Controller(T model, U view)
        {
            this._model = model;
            this._view = view;
        }

        public string ParentFunction()
        {
            return "I'm the parent";
        }
    }
}

注意,我还有一个模型和视图基类。由于暂时是空的,我就不给你看代码了

然后,我可以定义我的子类。例如,我将制作一个 PageController、PageModel 和 PageView。它们都将继承自它们的 BaseClass。

注意:PageModel 和 PageView 再次为空。它们仅用于继承

PageController.cs

using Inheritance.Page;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Inheritance.Classes
{
    public class PageController : Controller<PageModel, PageView>
    {
        public PageController():base()
        {

        }

        public PageModel Model
        {
            get
            {
                return base._model;
            }
        }
    }
}

如您所见,您将仅在 PageController 中指定 Model 类和 View 类。

要使用您的课程,您可以执行以下操作:

        PageController controller = new PageController();

        //We can access the parent function
        Console.WriteLine(controller.ParentFunction());

        //Function defined into the controller.
        PageModel model =  controller.Model;

【讨论】:

    【解决方案3】:

    我想这就是你想要的:

    public class GameModel : Model
    {
        public int ID { get; set; }
    }
    
    public class GameView : View<GameModel, GameView>
    {
        public float FOV { get; set; }
    }
    
    public class GameController : GameView.BaseControler
    {
        // Set ID
        public GameController()
        {
            Model.ID=100;
            View.FOV=45f;
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var gm = new GameModel();
            var view = new GameView();
            var ctrl = new GameController();
    
            view.Connect(gm, ctrl);
    
            Debug.WriteLine(view.Model.ID);
        }
    }
    

    public class Model
    {
    
    }
    
    public class View<TModel,TView> where TModel : Model where TView : View<TModel, TView>
    {
        public TModel Model { get; private set; }
        public BaseControler Controler { get; private set; }
    
        public void Connect(TModel model, BaseControler controler)
        {
            this.Model=model;
            this.Controler=controler;
            this.Controler.Connect(model, this as TView);
        }
        public class BaseControler
        {
            public TView View { get; private set; }
            public TModel Model { get; private set; }
    
            public void Connect(TModel model, TView view)
            {
                this.Model=model;
                this.View=view;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多