Вернуть более унаследованный общий класс из метода? - PullRequest
0 голосов
/ 09 ноября 2019

У меня есть такой интерфейс:

public interface IImportModel
{
}

И класс, который реализует этот интерфейс:

public class MaterialImportModel: IImportModel
{
    public string Name { get; set; }
}

Также у меня есть интерфейс для процессоров импорта:

public interface IImportProcessor<TModel> where TModel: IImportModel
{
    void Process(TModel model);
}

И есть одна из реализаций этого интерфейса:

public class MaterialImportProccessor : IImportProcessor<MaterialImportModel>
{
    public void Process(MaterialImportModel model)
    {
        // do some logic here
    }
}

Теперь я хочу создать Factory для создания экземпляров таких процессоров. У меня есть интерфейс:

public interface IImportProcessorFactory
{
    IImportProcessor<IImportModel> Get(Parameter parameter);
}

И я пытаюсь создать реализацию:

    public class ImportProcessorFactory : IImportProcessorFactory
    {
        public IImportProcessor<IImportModel> Get(Parameter parameter)
        {
             switch (Parameter) 
             {
                case "Materials":
                     IImportProcessor<IImportModel> processor = new MaterialImportProccessor();
                     return processor;

                case "Companies":
                    IImportProcessor<IImportModel> processor = new CompaniesImportProccessor();
                    return processor;
             }
        }
    }

Но я получил исключение:

Ошибка CS0266 Не удается неявно преобразоватьвведите 'MaterialImportProccessor' в IImportProcessor<IImportModel>. Существует явное преобразование (вам не хватает приведения?)

Это правильно. Но я не могу использовать ключевое слово out для создания IImportProcessor ковариации, потому что я использую TModel в качестве входного параметра метода Process(TModel method).

Есть ли способы реорганизовать этот код, чтобы он работал?

РЕДАКТИРОВАНИЕ

Я решил предоставить дополнительную информацию о том, как я планирую использовать эту фабрику.

var deserializer = _deserializerFactory.Get(/*some parameters*/);
var importProcessor = _importProcessorFactory.Get(someEunmValue);

var data = deserializer.Deserialize(file);
importProcessor.Process(data);

Из-за этого я не могу сделать фабрику процессора импорта общей.

Ответы [ 5 ]

0 голосов
/ 09 ноября 2019

Я решил добавить дополнительный абстрактный класс:

public abstract class ImportProcessor<TModel> : IImportProcessor<MaterialImportModel> where TModel: class
{
    public void Process(IImportModel model)
    {
        Process(model as TModel);
    }

    public abstract void Process(TModel model);
}

и модифицированный MaterialImportProccessor

public class MaterialImportProccessor : ImportProcessor<MaterialImportModel>
{
    public override void Process(MaterialImportModel model)
    {

    }
}
0 голосов
/ 09 ноября 2019

Обновление 2

Это лучшее, что я могу сегодня.

public interface IImportProcessor
{
     void Process();
}

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

public class ImportProcessor1 : IImportProcessor
{
    private readonly ImportModel1 data;
    public ImportProcessor1(ImportModel1 data) { this.data = data;}
    public void Process() => Console.WriteLine($"My name is {data.Name}");
}

public class ImportModel2{ public int Age { get; set; }}

public class ImportProcessor2 : IImportProcessor
{
    private readonly ImportModel2 data;

    public ImportProcessor2(ImportModel2 data) { this.data = data;}
    public void Process() => Console.WriteLine($"My age is {data.Age}");
}

public enum EType {One = 1, Two = 2}


public class ImportProcessorFactory
{
    public IImportProcessor Get(EType type, string file) 
    {   
        switch (type)
        {
            case EType.One: return new ImportProcessor1(JsonConvert.DeserializeObject<ImportModel1>(file));
            case EType.Two: return new ImportProcessor2(JsonConvert.DeserializeObject<ImportModel2>(file));
        }

        throw new NotImplementedException("Unable to work with '{type}'");
    }
}


class Program
{

    public static void Main() 
    {
        var f = new ImportProcessorFactory();

        var data1 = (EType.One, "{ Name: 'Vlad' }");
        var p1 = f.Get(data1.Item1, data1.Item2 );
        Console.WriteLine(p1.GetType());
        p1.Process();

        var data2 = (EType.Two, "{ Age: '20' }");

        var p2 = f.Get(data2.Item1, data2.Item2 );
        Console.WriteLine(p2.GetType());
        p2.Process();


    }
}

Выход

ImportProcessor1
My name is Vlad
ImportProcessor2
My age is 20

Обновление 1

Удалите интерфейс модели и заставьте процессор работать с T

public interface IImportProcessor<T>
{
    void Process(T model);
}

С помощью этой настройкидолжен держаться вместе.

public interface IImportProcessor<T>
{
    void Process(T model);
}

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

public class MaterialImportProccessor : IImportProcessor<MaterialImportModel>
{
    public void Process(MaterialImportModel model)
    {
        // do some logic here
    }
}

public interface IImportProcessorFactory<T>
{
    IImportProcessor<T> Get();
}

public class ImportProcessorFactory : IImportProcessorFactory<MaterialImportModel>
{
    public IImportProcessor<MaterialImportModel> Get() => new MaterialImportProccessor();
}

(Старый ответ)

Думаю, если сделать IImportProcessor неуниверсальным, ваша модель будет намного проще и устранит целый класс проблем.

public interface IImportProcessor
{
    void Process(IImportModel model);
}

Обработчик материалов должен реализовать Process, который принимает интерфейс, а не конкретный класс. В противном случае это а) не соответствует контракту и б) вы избавляетесь от преимуществ интерфейсов:).

public class MaterialImportProcessor : IImportProcessor
{
    public void Process(IImportModel model)
    {
        // do some logic here
    }
}
0 голосов
/ 09 ноября 2019

Вы должны изменить на:

public interface IImportModel
{

}

public class MaterialImportModel: IImportModel
{
    public string Name { get; set; }
}

public class CompanieImportModel: IImportModel
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public interface IImportProcessor
{
     void Process(IImportModel model);
}

public class MaterialImportProccessor : IImportProcessor
{
    public void Process(IImportModel model)
    {
        var obj = (MaterialImportModel) model;

        // do some logic here
    }
}

public interface IImportProcessorFactory
{
    IImportProcessor Get();
}


public class ImportProcessorFactory 
{
    public IImportProcessor Get(Parameter parameter)
    {
         switch (Parameter) 
         {
            case "Materials":
                 IImportProcessor processor = new MaterialImportProccessor();
                 return processor;

            case "Companies":
                IImportProcessor processor = new CompaniesImportProccessor();
                return processor;
         }
    }
}
0 голосов
/ 09 ноября 2019

ОБНОВЛЕНИЕ

https://dotnetfiddle.net/HYtWiN

рассмотрите возможность использования ключевого слова dynamic для своей фабрики:

    public interface IImportProcessorFactory 
    {
        dynamic Get(Parameter parameter);
    }

    public class ImportProcessorFactory : IImportProcessorFactory
    {
        public dynamic Get(Parameter parameter)
        { 
            switch (parameter)
            {
                case Parameter.Materials:
                    return new MaterialImportProccessor() ; 

                case Parameter.Companies:
                    return new CompaniesImportProccessor();
                default:
                    return null;
            }
        }
    }

, чтобы вы могли использоватьэто похоже на это:

        var factory = new ImportProcessorFactory();
        var material = factory.Get(Parameter.Materials);
        var company = factory.Get(Parameter.Companies);


        var model = new MaterialImportModel();
        model.MaterialName = " Metal ";
        material.Process(model);

        var cModel = new CompanyImportModel();
        cModel.CompanyName = "Build Metal Company";
        company.Process(cModel);

наслаждайтесь!

0 голосов
/ 09 ноября 2019

Вы пытались добавить приведение типа указанного пользователя DevFlamur, используя оператор as?

...