C# основанный на дженериках объект возразить вопросу о картопостроителе

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

public interface IMapper<T, R> {
    T Map(R obj);
}

Я затем реализую AccountMapper, который отображает Клиента на Учетную запись как:

public class AccountMapper : IMapper<Account, Customer> {
    Account Map(Customer obj) {
        // mapping code
    }
}

Это хорошо работает до сих пор, однако у меня есть несколько исходных объектов, которые отображаются на тот же целевой объект. Например, у меня есть Оплата и Счет что обе карты к BillHistory. Чтобы вышеупомянутое поддерживало это, я должен сделать два отдельных картопостроителя (т.е. BillHistoryPaymentMapper и BillHistoryInvoiceMapper), который прекрасен. Однако я хотел бы смочь реализовать его немного по-другому как ниже. Только проблема, я не знаю, возможно ли это и если так, я не знаю правильного синтаксиса.

public interface IMapper<T> {
    T Map<R>(R obj);
}

public class BillHistoryMapper : IMapper<Account> {
    public BillHistory Map<Invoice>(Invoice obj) {
        // mapping code
    }
    public BillHistory Map<Payment>(Payment obj) {
        // mapping code
    }
}

В то время как первая реализация хорошо работает, второе было бы немного более изящным. Действительно ли это возможно и раз так на что был бы похож правильный синтаксис?

править-------

Я ненавижу, когда люди делают это, но конечно я забыл упоминать тот мало детали. У нас есть абстрактный класс между картопостроителем и интерфейсом для реализации некоторой общей логики через все картопостроители. Таким образом, моя подпись картопостроителя на самом деле:

public class BillHistoryMapper : Mapper<BillHistory, Invoice> {
}

где Картопостроитель содержит:

public abstract class Mapper<T, R> : IMapper<T, R> {
    public IList<T> Map(IList<R> objList) {
        return objList.ToList<R>().ConvertAll<T>(new Converter<T, R>(Map));
    }
}
13
задан Brian 13 May 2010 в 19:43
поделиться

4 ответа

Вам придется использовать свой первый интерфейс и реализовать его несколько раз на вашем объекте:

public class BillHistoryMapper : IMapper<Account, Invoice>, 
                                 IMapper<Account, Payment> {     
   ...
}

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

4
ответ дан 2 December 2019 в 02:05
поделиться

Ваш второй пример будет работать только с несколькими изменениями:

// you have to include the R type in the declaration of the Mapper interface
public interface IMapper<T, R> {
    T Map<R>(R obj);
}

// You have to specify both IMapper implementations in the declaration
public class BillHistoryMapper : IMapper<Account, Invoice>, IMapper<Account, Payment> {
    public BillHistory Map<Invoice>(Invoice obj) {
        // mapping code
    }
    public BillHistory Map<Payment>(Payment obj) {
        // mapping code
    }
}

Я не уверен, что это действительно принесет вам что-нибудь по сравнению с существующий шаблон, тем не менее.

0
ответ дан 2 December 2019 в 02:05
поделиться

Если существует ограниченное количество типов, из которого вы хотите сопоставить, то я бы использовал первый метод объявления типов ввода и вывода в определении интерфейса. Затем картограф может реализовать интерфейсы для каждого типа ввода, который он поддерживает, поэтому ваш BillHistoryMapper будет объявлен как:

public class BillHistoryMapper : IMapper<BillHistory, Invoice>, IMapper<BillHistory, Payment>
{
   ...
}
0
ответ дан 2 December 2019 в 02:05
поделиться

Что касается вашего абстрактного класса, подумайте о том, чтобы избавиться от него и заменить его методом расширения. Это позволит вам использовать функцию MapAll независимо от того, реализуете ли вы интерфейс или используете какую-то цепочку наследования.

public static class MapperExtensions
{
    public static IEnumerable<TOutput> MapAll<TInput, TOutput>
        (this IMapper<TInput, TOutput> mapper, IEnumerable<TInput> input)
    {
        return input.Select(x => mapper.Map(x));
    }
}

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

public class BillHistoryMapper :
    IMapper<Invoice, BillHistory>, IMapper<Payment, BillHistory>
{
    public BillHistory Map<Invoice>(Invoice obj) {}
    public BillHistory Map<Payment>(Payment obj) {}
}

Также подумайте о том, чтобы изменить ваши общие параметры IMapper на противоположные (я сделал это в предыдущих примерах):

public interface IMapper<in TInput, out TOutput>
{
    TOutput Map(TInput input);
}

Причина этого в том, что он напрямую отображается на System.Converter делегат, и вы можете сделать что-то вроде:

IMapper<ObjectA, ObjectB> myAToBMapper = new MyAToBMapper();

ObjectA[] aArray = { new ObjectA(), new ObjectA() };
ObjectB[] bArray = Array.ConvertAll<ObjectA, ObjectB>(aArray, myAToBMapper.Map);

List<ObjectA> aList = new List<ObjectA> { new ObjectA(), new ObjectA() };
List<ObjectB> bList = aList.ConvertAll<ObjectB>(myAToBMapper.Map);

// Or

var aToBConverter = new Converter<ObjectA, ObjectB>(myAToBMapper.Map);
bArray = Array.ConvertAll(aArray, aToBConverter);
bList = aList.ConvertAll(aToBConverter);

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

public class AutoMapperWrapper<in TInput, out TOutput> : IMapper<TInput, TOutput>
{
    public TOutput Map(TInput input)
    {
        return Mapper.Map<TOutput>(input);
    }
}

Заключительное слово

Также помните, что не всегда ваша стратегия отображения будет работать во всех случаях, поэтому не пытайтесь бороться с вашим доменом и заставлять его соответствовать вашей стратегии отображения. Один из конкретных примеров - вам может потребоваться создать карту из двух входных элементов в один. Вы, конечно, можете сделать так, чтобы это соответствовало вашей стратегии, но вы можете обнаружить, что это становится беспорядочным. В этом конкретном примере рассмотрим слияние.

1
ответ дан 2 December 2019 в 02:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: