2017-01-06 2 views
0

Я пытаюсь создать общий конвертер типов. Все типы будут иметь базовый класс Entity и интерфейс IConvertable. Я реализовал интерфейс для объектов, которые могут быть преобразованы:Какова правильная конструкция для замены статического вызова на общие типы?

public interface IConvertable 
{ 
    public bool CanConvert<TTarget>() where TTarget : Entity, IConvertable 
    Tuple<bool, Entity> TryConvertTo<TTarget> where TTarget: Entity, IConvertable 
} 

Идея заключается в том, что каждый конкретный подкласс Entity (partial классов, автоматически сгенерированный код, я не могу напрямую изменять) будет иметь CanConvert<TTarget>, который определит, может ли он быть преобразован в целевой тип.

Я пытался собрать общий тип преобразователя, который будет принимать IEnumerable х Entity, превращая их (и выполнение других задач, в то же время - например, через другие интерфейсы, reparenting), а затем возвращаются преобразованная Entity классы.

Моя первоначальная реализация в том, что, по сути, CanConvert<TTarget> является static, в том, что она всегда постоянна от А TSource к TTarget, поэтому, когда я строю свой конвертер:

public class EntityConverter<TSource, TTarget> where TSource : Entity, IConvertable 
               where TTarget : Entity, IConvertable 
{ 
    private IEnumerable<TSource> Records { get; set; } 

    public EntityConverter(IEnumerable<TSource> records) { 
     if (!TSource.CanConvert<TTarget>()) { 
      // error condition, reporting, etc. 
     } 

     Add(records); 
    } 

    public void Add(IEnumerable<TSource> records) { ... } 

    public void Convert() { 
     // massively simplified for example - there will actually be a store 
     // holding, more functionality, parent-child relationships, but 
     // this is the basic principle 
     for (var c in convertables) { 
      c.TryConvertTo<TTarget>(); 
     } 
    } 
} 

Очевидно, что есть много причин, почему static функции не могут использоваться на дженериках (Eric Lipperts отличные описания из этого доступны onhisblog), однако это оставляет меня в quandry - как я могу определить что-то, что описывает тип (не экземпляр) и может ссылаться на тип, в общей структуре, без массивные внешние partial конструкции или злые фабрики - это предполагается быть доступной, повторно используемой структурой, чем кто-либо может звонить из другого класса, просто расширяя типы partial с реализацией интерфейса.

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

Редактировать Немного обновили образец кода (ранее было преобразование в конструкторе). На самом деле, я делаю это преобразование совсем немного позже, поскольку у меня есть несколько источников конвертируемых файлов, которые необходимо добавить до фактического преобразования. Это приводит к фактической ошибке (если !CanConvert<TTarget>()), идущей довольно далеко по линии. Для каждого экземпляра этого класса он будет преобразовываться из типа TSource в TTarget, поэтому справедливость этого преобразования должна быть известна в конструкторе, и это должно быть ошибкой в ​​конструкторе, а не во время преобразования позже ... По крайней мере , это мое чтение о вещах.

Благодаря @Dennis за разъяснение некоторых из моих мыслей о вещах ... Я могу закончить все это, но я не чувствую, что я далеко. Я подумал о том, чтобы поставить CanConvert<TTarget>() в функцию Add() ... которая чувствует, что я почти там ...

+1

Что не так с просто вызовом 'CanConvert' для каждого экземпляра в' convertables'? Это было бы более правильным, потому что некоторая 'TBase' не может быть преобразована в' TSomething', тогда как 'TDerived: TBase'. –

+1

примечание стороны: не использовать кортеж. 'bool TryConvertTo (вне результата Entity)' является лучшим дизайном –

+1

Кроме того, я не уверен, что переход 'convertables' в конструктор - лучший подход. Вероятно, это должен быть метод. И ваши заметки о других операциях («на самом деле есть магазин // холдинг, больше функциональности») предполагают возможное нарушение «EntityConverter» https://en.wikipedia.org/wiki/Single_responsibility_principle. Конвертер должен конвертировать только то, что предлагает его название. –

ответ

1

С моей точки зрения, это кажется задачей для AutoMapper. Вы хотите установить свойства с объекта на другой. Это все.

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

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

+0

Большое спасибо. Я посмотрел на Automapper, и похоже, что 95% того, что мне требуется для процесса преобразования; остальное можно сделать в моем собственном коде. Как вы говорите, найти хорошее решение проблемы, даже если это не то, что было задано, часто лучше, чем перенастройка решения, которое работает, но не оптимальное или сложнее поддерживать. –

+1

@PeterStreet Да! Вот и все ... Некоторые инструменты не выглядят так, как мы бы сделали сами, но нам нужно сосредоточиться на собственном домене, а не пытаться решить все проблемы в одном проекте :) –

1

Это слишком сложно.

Будет достаточно, чтобы держать TryConvertTo в IConvertable. Просто верните false, если тип цели не применим. Да, это будет одинаково для всех случаев, но что здесь не так?

Обратите внимание, что вы нарушаете хорошо известный шаблон для методов Try... при возврате кортежа.ИМО, это будет более знакомо:

public interface IConvertable 
{ 
    bool TryConvertTo<TTarget>(out TTarget entity) 
     where TTarget: Entity, IConvertable 
} 
+0

Я готовлю этот код для C# 7 с выводом named-tuple, который мне гораздо проще использовать и работать, чем неуклюжие и часто используемые типы out. Конечно, вы правы - шаблон хорошо известен, но это не значит, что это правильно: D Тем не менее, я понимаю, что выполнение TryConvertTo без ограничения Constructor будет работать, возможно ... Спасибо. –

+1

@PeterStreet. Другой альтернативой является возврат некоторого значения ['Опция '] (https://github.com/nlkl/Optional) - это позволяет избежать как дополнительных параметров 'out', так и pre C# 7 (например, имена неиндексных свойств). –

+0

Вопрос: учитывая, что фактическое преобразование в окончательной программе может быть значительно дальше от построения метода - я надеялся «не выполнить ранний», если бы вводились неконвертируемые типы, до выполнения других операций. Есть ли способ справиться с этим? –

Смежные вопросы