Estou trabalhando neste artigo do Xaml Brewer e alterando-o ligeiramente para usar meus modelos. Ele usa uma classe base MasterDetailViewModel genérica do tipo T.
A classe base é declarada assim
public abstract partial class MasterDetailViewModel<T> : ObservableObject
{
private readonly ObservableCollection<T> items = new();
etc...
Minha classe derivada declara como .
public partial class HomePageViewModel : MasterDetailViewModel<Tenant>
{
etc..
A classe Tenant é
[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..
Minha classe derivada contém um RelayCommand e um método
[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
}
}
Quero mover o método RelayCommand e DeleteCommand_Executed para a classe base. Se eu apenas mover o comando relay para a classe base e adicionar isso, protected abstract void DeleteCommand_Executed(int parm);
então com isso na classe derivada, protected override void DeleteCommand_Executed(int parm) etc.
tudo funciona bem, mas se eu mover DeleteCommand_Executed para a classe base, recebo este erro
Erro (ativo) CS1061 'T' não contém uma definição para 'Id' e nenhum método de extensão acessível 'Id' aceitando um primeiro argumento do tipo 'T' pôde ser encontrado (está faltando uma diretiva using ou uma referência de assembly?)
Eu tentei adicionar um identificador de tipo. var toBeDeleted = Items.First<T>(c => c.Id == parm);
Eu olhei algumas perguntas semelhantes no SO e alguns itens pesquisados no Google e acho que o que me disseram é que o T não é conhecido até que a expressão tenha sido executada, então ele não pode saber quais propriedades T tem? Então é possível atingir meu objetivo de colocar tudo na classe base?
desde já, obrigado
Primeiro, defina uma interface como abaixo:
Então, redefina
MasterDetailViewModel
asTenant
classes assim:Agora, mover o
DeleteCommand_Executed
método para aMasterDetailViewModel
classe deve funcionar bem.Você está recebendo o erro porque o compilador não consegue inferir que uma classe usada como
T
terá umId
- pode ser qualquer coisa neste ponto.Uma opção seria introduzir uma restrição genérica em
T
such como na outra resposta de Hadi Fooladi Talari. Isso tornaT
um pouco menos genérico porque você teria que manter um certo contrato, seja tendo a mesma classe como base para todosT
os s ou que todosT
os s que são válidos devem implementar uma interface.Outra opção seria ter uma
abstract
propriedade seletora na classe base do tipoFunc<T,int,bool>
- dessa forma, você dependeria desse seletor na classe abstrata sem precisar impor nenhuma restriçãoT
: