Определение универсального типа в базовом классе

У меня есть следующие классы. AuthorTest и BookTest наследуются от MyBaseClass, у которого есть метод AddName, принимающий общий параметр.

public class Book {
    public string Name {get;set;}
}

public class Author {
    public string FirstName {get;set;}
    public string LastName {get;set;}
}

public class BookTest : MyBaseClass {
    Book book = // book object

    base.AddName<Book>(book);
}

public class AuthorTest : MyBaseClass {
    Author author = // author object

    base.AddName<Author>(author);
}

public interface IMyBaseClass {
    void AddName<T>(T item);
}

public class MyBaseClass : IMyBaseClass {
    public void AddName<T>(T item) {
        // item could either be Book or Name
        var name = item.Name

        // or

        var name = item.FirstName + " " item.LastName;
    }
}

Как в базовом классе определить, какой тип T (книга или автор) передается? И на основе этого мне нужно построить переменную name. Есть ли элегантный способ сделать это?


person blankface    schedule 07.08.2020    source источник
comment
Если AddName имеет разные реализации для разных производных типов, похоже, вы хотите сделать его абстрактным или виртуальным. Если вы обнаружите, что хотите вызвать typeof(T), это становится чрезвычайно вероятным, что вам вообще не нужны дженерики.   -  person TheGeneral    schedule 07.08.2020
comment
Отвечает ли это на ваш вопрос? Проверка параметра типа универсального метода в C#   -  person devNull    schedule 07.08.2020
comment
Другим вариантом может быть создание перегрузок метода для каждого типа, который вы хотите поддерживать. Хотя трудно определить, будет ли работать альтернативное решение, не зная больше о том, как будет использоваться этот код.   -  person devNull    schedule 07.08.2020


Ответы (2)


Хотя и ваша иерархия, и использование дженериков кажутся весьма сомнительными, простой ответ на ваш вопрос заключается в том, что вы выполняете проверку типа во время выполнения так же, как и для параметра с общим типом, точно так же, как и для другого значения.

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;
    }
}
person Aluan Haddad    schedule 07.08.2020

Я чувствую, что вам лучше переместить параметр универсального типа из метода в сам тип. Вы пишете BookTest, а тест связан с Book, тогда более естественно определить ваш тип следующим образом: class BookTest : MyBaseClass<Book>.

Итак, вот базовые классы:

public interface IMyBaseClass<T>
{
    void AddName(T item);
}

public abstract class MyBaseClass<T> : IMyBaseClass<T>
{
    protected abstract string GetName(T item);
    
    public void AddName(T item)
    {
        var name = this.GetName(item);
    }
}

Затем вы пишете свои тесты следующим образом:

public class BookTest : MyBaseClass<Book>
{
    protected override string GetName(Book item)
    {
        return item.Name;
    }
}

public class AuthorTest : MyBaseClass<Author>
{
    protected override string GetName(Author item)
    {
        return $"{item.FirstName} {item.LastName}";
    }
}

Все строго типизировано, и у каждого класса есть четкая ответственность.

person Enigmativity    schedule 25.08.2020