我正在研究这篇Xaml Brewer文章,并对其进行了少许修改以使用我的模型。它使用 T 类型的通用 MasterDetailViewModel 基类。
基类声明如下
public abstract partial class MasterDetailViewModel<T> : ObservableObject
{
private readonly ObservableCollection<T> items = new();
etc...
我的派生类声明为。
public partial class HomePageViewModel : MasterDetailViewModel<Tenant>
{
etc..
类别承租人是
[Table("tenants")]
public partial class Tenant
{
//[Column("email")]
public string Email { get; set; }
//[Column("first_name")]
public string FirstName { get; set; }
[Key]
public int Id { get; set; }
etc..
我的派生类包含一个 RelayCommand 和一个方法
[RelayCommand]
private void Delete(int param) //param is Id of selected list item
{
DeleteCommand_Executed(param);
}
private void DeleteCommand_Executed(int parm)
{
if (parm > 0)
{
var toBeDeleted = Items.First(c => c.Id == parm); //get the record with this Id
DeleteItem(toBeDeleted); //delete from the database
}
}
我想将 RelayCommand 和 DeleteCommand_Executed 方法移到基类中。如果我只将中继命令移到基类中并添加此方法,protected abstract void DeleteCommand_Executed(int parm);
那么在派生类中protected override void DeleteCommand_Executed(int parm) etc.
一切都会正常,但如果我将 DeleteCommand_Executed 移到基类中,我会收到此错误
错误(主动)CS1061‘T’不包含‘Id’的定义,并且找不到接受类型‘T’的第一个参数的可访问扩展方法‘Id’(您是否缺少使用指令或程序集引用?)
我尝试添加类型标识符,var toBeDeleted = Items.First<T>(c => c.Id == parm);
我在 SO 上查看了一些类似的问题,并在 Google 上搜索了一些项目,我认为我被告知的是,在表达式运行之前 T 是未知的,因此它无法知道 T 具有哪些属性?那么,是否有可能实现将所有内容放入基类的目标?
提前致谢
首先,定义一个如下的接口:
然后,重新定义
MasterDetailViewModel
和Tenant
类,如下所示:现在,将
DeleteCommand_Executed
方法移至类中MasterDetailViewModel
应该可以正常工作。您收到错误是因为编译器无法推断用作的类
T
将具有Id
- 此时它可以是任何东西。一种选择是引入通用约束,
T
如 Hadi Fooladi Talari 的另一个答案中所述。这会使T
通用性稍差一些,因为您必须维护某些约定,要么让所有T
s 都使用相同的类作为基类,要么所有T
有效的 s 都应实现一个接口。另一种选择是
abstract
在类型的基类上有一个选择器属性Func<T,int,bool>
- 这样您就可以依赖抽象类中的这个选择器而不需要施加任何约束T
: