泛型类约束,其中<T>是约束泛型类的类型。

3
也许这不是最准确的标题,但有点难以描述;也许你们能在这里帮我吗? 我正在使用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; 
  }
}

正如您所看到的,在基类中这很快变得混乱。我不知道要为(参考上面的示例)试图定义约束的"Model"放置什么符号。将"Model"放入问号中似乎也无法编译,因为我遇到了一个可怕的装箱转换问题。
有没有办法实现我想要的效果,还是我只是在尝试过于聪明?如果这能够奏效,我很想能够声明子类类型受限于它们的“三角形”,因此我可以避免不必要的转换或帮助方法:
public class ChildModel<ChildView, ChildController> {

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

有人有任何想法吗?

(这段文本已翻译)

相关链接:https://dev59.com/hXRB5IYBdhLWcg3wXWK2#25166761 和 https://dev59.com/_2445IYBdhLWcg3w6ebz#4632320 - John Alexiou
3个回答

1

看起来你把所有权和交互混淆了。所有权意味着一个拥有另一个,而交互则意味着它们如何相互通信。MVC主要定义了三个参与者之间的交互,尽管你可以说一个视图和控制器都拥有一个模型。

enter image description here

在您展示的代码中,一个类拥有一个属性,因此控制器类拥有一个视图,而视图拥有一个控制器。
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框架提供了丰富的数据绑定功能,可以打破类之间的直接耦合,但如果没有这个功能,你需要编写或查找相关内容,或者按照语言规则进行操作。
有许多方法可以解决这个问题。当我在写这篇文章时,另一个答案已经发布了,所以你也应该看一下那个答案。

附带问题:您用哪个程序或工具绘制了那个图表? - Lasse V. Karlsen
非常感谢!我喜欢这个实现方式,我想我会继续使用它。谢谢你 :) - WTScott
我很高兴它有帮助。设计、开发、分享! - David Anderson

0
这是我的建议。 1. 你应该将控制器作为MVC模式的主要部分。控制器应该从模型中获取信息,处理它,然后调用视图。
这是我为控制器准备的基类。
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;

0

我认为这就是你想要的:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接