Как правильно реализовать метод универсального интерфейса?

#c# #generics #interface

#c# #дженерики #интерфейс

Вопрос:

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

Чего я пытаюсь добиться, так это: на основе некоторых входных данных (SomeModelA, SomeModelB) Я хочу получить тот же возвращаемый тип (шаблон).

 namespace GenericInterfacePuzzle
{
    class Program
    {
        static void Main(string[] args)
        {
            var workerA = new WorkerA();
            var itemsBasedOnModelA = workerA.Get(new List<SomeModelA>());

            var workerB = new WorkerB();
            var itemsBasedOnModelB = workerB.Get(new List<SomeModelB>());
        }
    }

    public interface IWorker
    {
        Template Get<T>(List<T> someModels);
    }

    public class WorkerA : IWorker
    {
        public Template Get<SomeModelA>(List<SomeModelA> someModels)
        {
            ProcessModels(someModels);
            return new Template(); // let's say it's based on the result of ProcessModels
        }

        private void ProcessModels(List<SomeModelA> models)
        {
            var x = models.First();
        }
    }

    public class WorkerB : IWorker
    {
        public Template Get<SomeModelB>(List<SomeModelB> someModels)
        {
            ProcessModels(someModels);
            return new Template(); // let's say it's based on the result of ProcessModels
        }

        private void ProcessModels(List<SomeModelB> models)
        {
            var x = models.First();
        }
    }

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

    public class SomeModelB
    {
        public string Age { get; set; }
    }
    public class Template
    {
        // Irrevelant return type
    }
}
  

Я хочу знать на уровне класса WorkerA / WorkerB, что я имею дело с конкретной моделью, и на основе этого я хочу вернуть экземпляр класса шаблона
Проблема в том, что в строках, вызывающих Process:

 ProcessModels(someModels);
  

Я получаю сообщение об ошибке:

Ошибка CS1503 Аргумент 1: не удается преобразовать из ‘System.Коллекции.Универсальный.Список SomeModelA’ to’System.Коллекции.Универсальный.Список GenericInterfacePuzzle.SomeModelA’

Любая обратная связь приветствуется, что здесь может быть не так, и почему он не распознает классы модели при передаче функциям.

Крис

Комментарии:

1. в какой строке вы получаете ошибку, кстати?

2. Вы никогда не используете свой workerB btw

3. Общий параметр типа должен быть в интерфейсе, а не в методе: public interface IWorker<T> Затем вы можете реализовать то, что хотите : public class WorkerA : IWorker<SomeModelA> .

4. У вас есть 2 класса с одинаковыми именами SomeModelA , поэтому вы получили исключение выше. Попробуйте так => var itemsBasedOnModelA = worker.Get(new List<GenericInterfacePuzzle.SomeModelA>());

Ответ №1:

1) Вам необходимо определить общий параметр на уровне вашего интерфейса. В противном T случае параметр не известен компилятору:

 public interface IWorker<T> where T: SomeModel
{
    Template Get(List<T> someModels);
}
  

2) вам нужно установить ограничение, поскольку вы, вероятно, не хотите, чтобы вашему интерфейсу присваивался какой-либо тип. Было бы предпочтительнее создать базовый класс для ваших моделей и позволить им наследовать от него:

 public abstract class SomeModel { ... }    

public class SomeModelA : SomeModel
{
    public string Name { get; set; }
}

public class SomeModelB : SomeModel
{
    public string Age { get; set; }
}
  

Таким образом, это позволит вам указать модель непосредственно в объявлении класса, который будет реализовывать интерфейс (см. Пункт 3)

3) Теперь вам нужно указать в дочерних классах, какая модель принадлежит какому workertype:

 public class WorkerA : IWorker<SomeModelA>
{
    public Template Get(List<SomeModelA> someModels)
    {
        ProcessModels(someModels);
        return new Template(); // let's say it's based on the result of ProcessModels
    }

    private void ProcessModels(List<SomeModelA> models)
    {
        var x = models.First();
    }
}

public class WorkerB : IWorker<SomeModelB>
{
    public Template Get(List<SomeModelB> someModels)
    {
        ProcessModels(someModels);
        return new Template(); // let's say it's based on the result of ProcessModels
    }

    private void ProcessModels(List<SomeModelB> models)
    {
        var x = models.First();
    }
}
  

Вы также должны удалить общую спецификацию в своем Get методе!

 public Template Get<SomeModelA>(List<SomeModelA> someModels)
                      ^
                      |
                   remove this
  

это уже указано при реализации интерфейса:

 public class WorkerA : IWorker<SomeModelA>
  

4) и последнее, что вы тестируете в основном методе:

 var worker = new WorkerA();
var itemsBasedOnModelA = worker.Get(new List<SomeModelA>());

var workerB = new WorkerB();
var itemsBasedOnModelB = worker.Get(new List<SomeModelB>());
                           ^
                           |
                    this should be [workerB]!