Вывод типов при использовании универсальных интерфейсов на фабрике - PullRequest
2 голосов
/ 13 ноября 2011

Мои коллеги и я собираем небольшую систему отчетности для интернет-магазина.Мы создали библиотеку по образцу репозитория, используя «отчеты» в качестве репозиториев и очень легкий сервисный слой для взаимодействия с указанными отчетами.Наш код прекрасно работает и довольно прост в использовании.Тем не менее, есть одна вещь, которая беспокоит меня лично: на уровне фабрики сервисов нам нужно дважды объявить наш тип возврата (это не выводится).Вот основа нашего проекта:

Интерфейс отчета

Это то, что используется в качестве наших «Хранилищ».Они принимают объекты доступа к данным, такие как классы-оболочки для соединения SQL / Oracle или API нашего Storefront.

internal interface IReport<T>
{
    T GetReportData(dynamic options);
}

Фабрика репозитория

Это обеспечиваетпростой способ создать эти отчеты, зная тип.

internal interface IReportFactory
{
    TR GenerateNewReport<T, TR>() where TR : IReport<T>;
}

internal class ReportFactory : IReportFactory
{
    public ReportFactory()
    {
        // some initialization stuff
    }

    public TR GenerateNewReport<T, TR>() where TR : IReport<T>
    {
        try
        {
            return (TR)Activator.CreateInstance(typeof(TR));
        }
        catch(Exception ex)
        {
            // Logging
        }
    }
}

Пример отчета (репозиторий)

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

internal class ItemReport : IReport<DataTable>
{
    public DataTable GetReportData(dynamic options)
    {
        return new DataTable();
    }
}

Служба отчетов

Облегченный сервис, который принимает отчет (репозиторий) и работает с ним.Легко, но позволяет легко выполнять модульное тестирование и тому подобное, и если мы когда-нибудь захотим добавить дополнительную обработку после получения отчетов, это будет легко сделать.

public interface IReportService<T>
{
    T GetReportData(dynamic options);
}

public class ReportService<T> : IReportService<T>
{
    private readonly IReport<T> _report;

    public ReportService(IReport<T> report)
    {
        _report = report;
    }

    public T GetReportData(dynamic options)
    {
        return _report.GetReportData(options);
    }
}

СлужбаФабрика

У нас есть фабрика обслуживания, настроенная как абстрактный класс (потому что все фабрики обслуживания должны будут создать фабрику отчетов), форсируя конструктор по умолчанию:

public abstract class ReportServiceFactory
{
    protected IReportFactory ReportFactory;

    protected ReportServiceFactory(connection strings and other stuff)
    {
        ReportFactory = new ReportFactory(connection strings and other stuff);
    }
}

Мы можемзатем создайте отдельные фабрики обслуживания на основе функции.Например, у нас есть фабрика обслуживания «стандартный отчет», а также фабрики по обслуживанию клиентов.Реализация здесь, где мой вопрос.

public class SpecificUserServiceFactory : ReportServiceFactory
{
    public SpecificUserServiceFactory(connection strings and other stuff) : base(connection strings and other stuff){}

    public IReport<DataTable> GetItemReport()
    {
        return new ReportService<DataTable>(ReportFactory.GenerateNewReport<DataTable, ItemReport>());
    }
}

Почему я должен быть настолько многословным при создании фабрики сервисов?Я объявляю тип возврата дважды.Почему я не могу сделать что-то вроде этого:

return new ReportService(ReportFactory.GenerateNewReport<ItemReport>());

Обратите внимание, что я не объявляю DataTable в этом;Я думаю, что это следует из того факта, что ItemReport является IReport.Будем весьма благодарны за любые предложения о том, как заставить его работать таким образом.

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

Ответы [ 2 ]

1 голос
/ 15 ноября 2011

Причина, по которой вы не можете опустить универсальный тип DataTable при вызове GenerateNewReport, заключается в том, что это ограничение другого универсального типа в определении этой функции.Давайте предположим (для простоты), что ваш IReport на самом деле был интерфейсом с функцией void Something (T input).Класс может реализовывать как IReport , так и IReport .Затем мы создаем такой класс, который называется Foo: IReport , IReport .Компилятор не сможет скомпилировать bar.GenerateNewReport , потому что он не знает, привязан ли он к типу IReport или IReport и, следовательно, не может определить соответствующий тип возврата для этого вызова.

1 голос
/ 13 ноября 2011

Не думаю, что компилятор сможет вывести DataTable из ItemReport. Но вы можете избежать указания DataTable дважды, используя статический универсальный метод в неуниверсальном классе.

ReportService.Create(reportFactory.GenerateNewReport<DataTable, ItemReport>())
public static class ReportService
{
    public static ReportService<T> Create<T>(IReport<T> report)
    {
        return new ReportService<T>(report);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...