通用方法 public T Method() where T : BaseViewModel always returns NULL

Generic Method public T Method() where T : BaseViewModel always returns NULL

我现在脑子放屁了。我正在尝试为我的所有控制器设置一个 BaseController。有了它,我想要一种方法来为所有继承自 BaseViewModel 的视图创建各种 ViewModel,并设置 BaseViewModel 中的属性。 BaseViewModel 中的属性是根据正在使用的控制器操作设置的,因此我不能只在 BaseViewModel 构造函数中设置它们。

然而,当我调用它时,我总是得到一个 Null 值。

这是BaseController中的方法:

/// <summary>
/// Instantiate a ViewModel
/// </summary>
/// <typeparam name="T">ViewModel that inherits <see cref="BaseViewModel"/></typeparam>
/// <returns></returns>
public T SetupViewModel<T>(int? currentTrack = null) where T : BaseViewModel
{
     // Return a new instance of a ViewModel
    return new BaseViewModel(_Context)
    {
         // Set the current track
         CurrentTrack = currentTrack,
    } as T;
}

我在 public class HomeController : BaseController:

中这样称呼它
/// <summary>
/// Main page
/// </summary>
/// <returns></returns>
public IActionResult Index()
{
    // Instantiate the view model
    var vm = SetupViewModel<HomeViewModel>();
 
    // Setup the repositories
    vm.SetupRepositories();

    // Load the tables base on the user
    vm.Load(GetLoggedInUserName());

    // Return the view with model
    return View(vm);
}

当我在vm.SetupRepositories()处中断时,变量vm为NULL。

编辑(回应关于 new() 的评论 当我尝试 new()

时我得到了这个

好的,回答你问题的第一部分,为什么这个是空的?

public T SetupViewModel<T>(int? currentTrack = null) where T : BaseViewModel

{
     // Return a new instance of a ViewModel
    return new BaseViewModel(_Context)
    {
         // Set the current track
         CurrentTrack = currentTrack,
    } as T;
}

将 return 为空,除非您的 'T' 是 BaseViewModel。如果可能,as 关键字将强制转换,但如果不能,则 return 将为空。您已经实例化了一个 BaseViewModel,但期望它转换为(我假设)它的子类型之一 'T'.

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast#as-operator


如何解决?

我建议您再次考虑对基本控制器的需求,并考虑您希望从这种方法中获得什么好处。

我建议您找到一些依赖注入示例,并使用构造函数注入设置您的存储库。

您的具体控制器必须负责设置您的视图模型映射,除非您将其进一步抽象为某种工厂。再次重申,请确保您知道通过这样做您试图获得什么好处——您的所有控制器是否足够相同且足够简单以使其有效。如果不是,请考虑另一种方法。

我过去发现,如果您采用的通用方法明显难以实施,通常表明存在可能更好的替代架构。

组合 vs 继承

不要过多地强调这一点,但根据我的经验,BaseViewModels 和 BaseController 类型的实现往往会遇到“脆弱的基础 class”问题。

https://en.wikipedia.org/wiki/Fragile_base_class#:~:text=The%20fragile%20base%20class%20problem,the%20derived%20classes%20to%20malfunction.

最好取消这两个基础 classes,而是让您的视图模型继承包含您的 'CurrenTrack' 的接口,并编写创建视图所需的行为模型到你的控制器而不是试图通过一个单一的基地强制它全部 class.

随着项目的推进,基础控制器往往会成为垃圾收集器,并且变得很难修复和进行单元测试。

所以正如我所想的那样,这是一件小事。感谢haim770(OP评论)指路

我的 BaseViewModel 有两个构造函数(一个默认构造函数和一个接受参数来设置上下文的构造函数。所以我只需要将上下文设置为 属性 而不是通过参数。另外,实例化一个新 T 而不是 BVM。

/// <summary>
/// Instantiate a ViewModel
/// </summary>
/// <typeparam name="T">ViewModel that inherits <see cref="BaseViewModel"/></typeparam>
/// <returns></returns>
public T SetupViewModel<T>(int? currentTrack = null) where T : BaseViewModel, new()
{
     // Return a new instance of a ViewModel
    return new T()
    {
         // Set the Context
         Context = _Context,

         // Set the current track
         CurrentTrack = currentTrack,
    } as T;
}