9

Какие существуют инструменты/библиотеки, которые будут занимать структуру и автоматически генерировать неизменяемую оболочку, а также класс «строитель» для постепенного создания новых экземпляров?Автоматическая генерация неизменяемого класса и класса сопоставимого класса

Пример входных данных:

struct Foo 
{ 
    public int apples; 
    public int oranges; 
    public Foo Clone() {return (Foo) base.MemberwiseClone();} 
} 

Пример вывода:

public class ImmutableFoo // could probably be a struct 
{ 
    private Foo snapshot; 
    internal ImmutableFoo(Foo value) { this.snapshot = value; } 
    public FooBuilder Builder() { return new FooBuilder(snapshot); } 
    public int Apples { get { return snapshot.apples; } } 
    public int Oranges { get { return snapshot.oranges; } } 
} 

public class FooBuilder 
{ 
    private Foo state; 

    public int Apples { get { return state.apples; } set { state.apples = value; } } 
    public int Oranges { get { return state.oranges; } set { state.oranges = value; } } 

    public FooBuilder() { } 

    internal FooBuilder(Foo toCopy) { state = toCopy.Clone(); } 

    public ImmutableFoo Build() 
    { 
     ImmutableFoo result = new ImmutableFoo(state); 
     state = state.Clone(); 
     return result; 
    } 
} 

Такой «инструмент» может быть IDE плагин или может генерировать новый класс во время выполнения с помощью отражения.

Пример в C#, но я был бы заинтересован в растворе для любой статически типизированного языка OO (Java, Scala, C++ и т.д.)

желателен Особенности:

  • воссоздает методы от структуры в классе строителя
  • Повторно создает неразрушающие методы из структуры в неизменном классе (ЭСП. Equals() и GetHashCode() и любой интерфейсных метод)
  • генерирует также в IFooReader, содержащий свойства только для чтения для каждого члена структуры, реализуемые как неизменяемыми, так и строителями.
  • Если класс поля имеет неизменный эквивалент, он использует неизменяемую версию в неизменяемом классе (см. Также How do I create a builder in C# for an object that has properties that are referenc types?), например. List ->ReadOnlyCollection или аналогичный.
  • В качестве альтернативы взять класс строитель в качестве входных данных (где строитель использует автоматические свойства вместо делегирования на структуру.)
  • Не требует метода Clone быть предопределен

«Вы не должны использовать инструмент как это, потому что ... »ответы также приветствуются.

ответ

4

Вот четыре возможных решения.

1) Используйте код CodeDOM для генерации кода C# или VB. Это также позволит вам использовать визуальные расширения студии для генерации кода в файлах конструктора. Подобно некоторым встроенным инструментам, которые уже предлагает визуальная студия - например, те, которые генерируют оболочки для вызовов веб-сервисов и т. Д. К сожалению, я мало знаю о расширении Visual Studio.

  • Плюсы - вы можете создать источник до строительства. Это упрощает запись кода с создаваемых типов из любой сборки.
  • Против - Неязычный агностик. Вы застряли с поддерживаемыми языками.

2) Используйте библиотеку Mono.Cecil, чтобы проанализировать сборку после сборки. Затем вы можете переписать сборку с включенными новыми типами.

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

3) Использовать PostSharp. Я не знаю столько об этой библиотеке, чтобы вы не могли добавлять новые типы в свою сборку, но я знаю, что вы можете вводить IL в методы. В нем также есть много приятных вещей, которые облегчают это с помощью атрибутов. Таким образом, вы могли бы сделать это -

[GenerateImmutable] 
struct Foo 
{ 
    public int apples; 
    public int oranges; 
    public Foo Clone() {return (Foo) base.MemberwiseClone();} 
} 
  • Pros - Язык агностик, AOP проще сделать в PostSharp.
  • Против - То же, что и с Mono.Cecil, а также не уверены, можете ли вы создавать новые типы с помощью PostSharp.

4) Используйте встроенные библиотеки Reflection.Emit для создания новой сборки с неизменяемыми типами.

  • Плюсы - язык агностик, нет предметов третьей стороны.
  • Против - Должны ставить сгенерированные типы в новые сборки. Не удается добавить их к той же сборке, в которой находится исходный тип.
+0

5) Используйте Roslyn для анализа вашего кода и t4 для создания построителя –

2

Зачем беспокоиться о застройщике?

У вас есть (отвратительная) измененная структура, но если вы должны использовать ее напрямую, а не создавать громоздкий и ненужный Builder.

Меня несколько беспокоит, что у вас есть достаточное количество этих структур, чтобы вы чувствовали, что вам нужно автогенерировать обертки такого типа. Моя инстинктивная реакция является то, что вы делаете это неправильно ...

Если цель неизменного обертки только для хранения снимков, то просто использовать что-то вроде этого:

public struct Snapshot<T> where t : struct 
{ 
    private readonly T data; 
    public Snapshot(T value) { this.data = value; } 
    public T Data { get { return data; } } 
} 

структура Передаваемый гарантируется никогда не меняться снова, но вы можете получить доступ ко всем значениям на нем напрямую (и модификации этих результатов происходят на копии, созданной при вызове базовой функции get_Data)

+0

Структура будет внутренней, но неизменяемый класс и класс строителя будут общедоступными. Ваша техника будет работать, но идеальное решение будет иметь эквивалент в Java (где нет типов значений, поэтому член-клон, вероятно, неизбежен.) – finnw

+0

@finnw Зачем делать структуру внутренней? Я думаю, вам нужно «подняться на уровень» и объяснить, как/почему эта идиома будет использоваться, чтобы мы могли лучше ее понять. Если вы проектируете вещи как «снимки», то просто сделайте их imutable, чтобы начать с создания вашего api формы foo.WithX (x) -> foo, где произошло изменение. то любая ссылка на конкретный экземпляр foo сама по себе является вполне допустимым моментальным снимком. – ShuggyCoUk

+2

@finnw Я бы сказал, что если вы беспокоитесь об автогенерации, чтобы вы могли быть похожими в java, почему бы просто не автогенерировать достойную неизменяемую реализацию, где каждое свойство/getFoo имеет только чтение, а значение WithhFoo (значение x), которое возвращает новый экземпляр объекта с измененным значением. Любой экземпляр объекта/структуры неизменен, у вас есть хороший свободный интерфейс для изменения вещей (подобно некоторым функциональным языкам программирования, поэтому разумно стандартизован). Не нужно строителям или неизменяемым версиям, работа выполнена. – ShuggyCoUk

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