Определение общего инициализатора для неизменной иерархии типов - PullRequest
1 голос
/ 22 апреля 2011

Я бы хотел определить интерфейс и абстрактный базовый класс для набора типов «Запись».Записи обычно передаются в / из другой системы, которая представляет запись в виде строки.Таким образом, каждому классу записи потребуется метод Parse(string str) и метод ToString().Классы записей также должны определять методы для сравнения на равенство, а также несколько других общих служебных методов.Пример класса записей без общей базы может выглядеть следующим образом:

public class MyRecord : IEquatable<MyRecord>
{
    public string FieldA { get; private set; }
    public int FieldB { get; private set; }

    public MyRecord(string fieldA, int fieldB /* .. */) { }
    public static MyRecord Parse(string recordString) { /* .. */ }

    public override string ToString() { /* .. */  }
    public override int GetHashCode() { /* .. */ }
    public override bool Equals(object obj) { /* .. */ }
    public bool Equals(MyRecord other) { /* .. */ }
}

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

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

public interface IRecord { /* .. */ }

public abstract class RecordBase : IRecord
{
    public static TRecord Parse<TRecord>(string recordStr)
        where TRecord: RecordBase, new()
    {
        TRecord record = new TRecord();
        record.Initialize(recordStr);

        return record;
    }

    protected abstract void Initialize(string recordStr);
}

public class MyRecord : RecordBase, IEquatable<MyRecord>
{
    public string FieldA { get; private set; }
    public int FieldB { get; private set; }

    public MyRecord(string fieldA, int fieldB /* .. */) { /* .. */ }

    public override string ToString() { /* .. */  }
    public override int GetHashCode() { /* .. */ }
    public override bool Equals(object obj) { /* .. */ }
    public bool Equals(MyRecord other) { /* .. */ }

    protected MyRecord() { }
    protected override void Initialize(string recordStr) { /* .. */ }
}

Однако компилятор жалуется, когда я пытаюсь вызвать RecordBase.Parse<MyRecord>(..), потому что конструктор по умолчанию MyRecord не доступен публично.

Итаку меня вопрос:

  • Есть ли лучший дизайн, который позволил бы мне иметь неизменяемые типы записей, а также обычный Parse инициализатор?Или в попытке создать неизменную иерархию типов с помощью общих API инициализации на уровне базового класса есть свойственный недостаток?

Ответы [ 3 ]

2 голосов
/ 22 апреля 2011

Вы нарушаете SRP при добавлении функций анализа для ваших записей.

  1. Создайте отдельный класс анализатора.
  2. Пометьте его атрибутом: [ParserFor (typeof (MyRecord))]]
  3. Создать класс ParserService.
  4. Использовать отражение для сканирования всех загруженных сборок после типов, имеющих атрибут ParserFor.
  5. Используйте службу парсера для разбора и создания всех записей.
0 голосов
/ 22 апреля 2011

Если вы обещаете быть очень осторожным, вы можете использовать System.Runtime.Serialization.FormatterServices.GetUninitializedObject . Однако при использовании этого метода потребуется гарантия того, что ни одна из реализаций IRecord.Parse не ожидает, что какое-либо поле (вообще) будет не нулевым / 0.

0 голосов
/ 22 апреля 2011

Я думаю, что вы ищете фабричный шаблон (анализ данных) и шаблон любопытного повторения шаблона (для способности клонирования / фабрики).

* 1003 Е.Г. *

abstract class Base<T> : IBase // IBase for common demoninator
{
    abstract T Parse(Stream data);
}

Derived : Base<Derived>
{

}
...