【问题标题】:Generic Method public T Method() where T : BaseViewModel always returns NULL通用方法 public T Method() 其中 T : BaseViewModel 总是返回 NULL
【发布时间】:2020-07-08 14:58:54
【问题描述】:

我现在正在放屁。我正在尝试为我的所有控制器设置一个 BaseController。有了这个,我想要一种方法来为所有从 BaseViewModel 继承的 View 创建各种 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;
}

我在公共类 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。

EDIT(回复 cmets 关于 new() 当我尝试 new() 时,我得到了这个

【问题讨论】:

  • 您的 HomeViewModel 是否继承自 BaseViewModel?
  • 您正在创建 BaseViewModel 的对象而不是 T 的对象。您不能将 BaseViewModel 对象转换为 T 对象,这就是您将其设为 null 的原因。
  • SetupViewModel() 方法正在创建一个BaseViewModel 实例,但在return 上它会将其称为T,在本例中为HomeViewModel。由于转换失败,它返回null。我希望您的方法是调用new T() 并通过添加另一个通用约束来启用它:where T : BaseViewModel, new()
  • @Nenad 是的; Chentan,我试过 return new T(_Context) 但这没有编译。我认为它会因为我说 T 必须继承 BaseViewModel ,它的构造函数中有它。
  • @Red_Phoenix,您不能将参数传递给构造函数。 new() 约束的含义是:“该类型有一个 parameter-less 构造函数”。尝试将 _Context 作为属性传递

标签: c# generics asp.net-core-mvc asp.net-mvc-viewmodel


【解决方案1】:

所以正如我所想,这是一件小事。感谢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;
}

【讨论】:

    【解决方案2】:

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

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

    除非您的“T”是 BaseViewModel,否则将返回 null。如果可能,as 关键字将强制转换,但如果不能,将返回 null。您已经实例化了一个 BaseViewModel,但希望它转换为(我假设)它的子类型之一“T”。

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


    如何解决?

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

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

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

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

    组合与继承

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

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

    最好去掉这两个基类,而是让您的视图模型从包含“CurrenTrack”的接口继承,并将创建视图模型所需的行为组合到控制器中,而不是试图强制这一切都通过一个基类实现。

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

    【讨论】:

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