Хотя и ваша иерархия, и использование универсальных шаблонов кажутся весьма сомнительными, простой ответ на ваш вопрос заключается в том, что вы выполняете проверку типа во время выполнения так же, как вы бы выполняли для типизированного параметра, как и для другого значения
public class MyBaseClass : IMyBaseClass
{
public void AddName<T>(T item)
{
// item could either be Book or Author
var name = item switch
{
Author a => $"{a.FirstName} {a.LastName}",
Book b => b.Name,
_ => throw new ArgumentException(nameof(item), $"Invalid type: {a?.GetType()}")
};
}
}
Однако, как уже указывали другие, поскольку вы поддерживаете только фиксированный набор типов, перегрузки были бы более подходящими.
public class MyBaseClass : IMyBaseClass
{
public void AddName(Author author)
{
var name = $"{author.FirstName} {author.LastName}";
}
public void AddName(Book book) {
var name = book.Name;
}
}