Как я могу решить эту проблему полиморфизма C #? - PullRequest
0 голосов
/ 25 ноября 2010

У меня есть базовый класс, SpecialClass. Многие другие классы наследуются от него, например WriteSpecialClass и ReadSpecialClass. Экземпляры этих классов сериализуются и десериализуются после приведения к базовому классу или из него. Поэтому у меня много повторяющегося кода, подобного этому (для сериализации):

SpecialClass sc = null;

if (type.DisplayName == "Read") {
    sc = new ReadSpecialClass();
} else if (type.DisplayName == "Write") {
    sc = new WriteSpecialClass();
} else
    throw new NotSupportedException("Type not supported");

String xml = SerializeToXML(sc);

.. и десериализация:

SpecialClass sc = null;

sc = (SpecialClass)DeserializeFromXML(xml);

switch (someVariableThatDicatesTypeOfSpecialClass) {
    case "Read":
        sc = (ReadSpecialClass)sc;
        break;
    case "Write":
        sc = (WriteSpecialClass)sc;
        break;
}

Еще одна важная информация: SpecialClass имеет набор абстрактных методов, которые должен реализовать каждый класс, который унаследован от него. Все классы, которые наследуют его, соответствуют методам, но возвращают разные вещи, поэтому каждый класс не одинаков.

При необходимости я могу опубликовать свои методы сериализации или десериализации.

Я хотел бы попытаться упростить это, чтобы мне не нужно было указывать каждый класс SpecialClass (например ReadSpecialClass). Есть какой-либо способ сделать это? Единственный способ, которым я могу придумать, - это набирать утку. Спасибо за вашу помощь,

Ответы [ 5 ]

3 голосов
/ 25 ноября 2010

Рассматривали ли вы метод serialize () в SpecialClass?Таким образом, если есть некоторые особые соображения, вы можете переопределить метод base.serialize и позаботиться о любых уникальных потребностях.Таким образом, он уже знает, что это такое, а условное выражение не требуется.

Еще одна вещь, которую следует учитывать, - это, возможно, вспомогательный класс с шаблоном фасада.Тогда у вас может быть метод с именем public publicClass DeserializeSpecialClass ().Тогда вместо того, чтобы наложить тип в десериализаторе, вы можете наложить его на цель.Если вы обнаружите, что выполняете слишком много приведения, возможно, стоит рассмотреть возможность добавления абстрактных методов в базовый класс, которые будут реализованы в производном классе.

Надеюсь, это поможет.

2 голосов
/ 25 ноября 2010

Для сериализации вы можете использовать некоторый вид поиска:

public class SpecialClass
{
    private static Dictionary<string, Func<SpecialClass>> factories =
        new Dictionary<string, Func<SpecialClass>>();

    static SpecialClass()
    {
        factories["Read"] = () => new ReadSpecialClass();
        factories["Write"] = () => new WriteSpecialClass();
    }

    public static SpecialClass CreateByName(string name)
    {
        Func<SpecialClass> factory;

        if (!factories.TryGetValue(name))
            throw new ArgumentException("name", "\"" name +
                "\" is not a recognized subclass of SpecialClass.");

        return factory();
    }
}

Для десериализации эти две строки:

sc = (ReadSpecialClass)sc;
sc = (WriteSpecialClass)sc;

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

object a = "foo";
a = (string)a;

Конечно, приведение будет успешным.Но это никоим образом не изменяет объект, на который указывает a.Все, что он действительно делает, это проверяет, что этот объект уже является строкой.

0 голосов
/ 25 ноября 2010

Правило 1. / Ваши производные классы должны отвечать за сериализацию и десериализацию самих себя, поскольку они должны быть единственными, кто обладает глубокими знаниями о дополнительных данных, хранящихся в документе XML.

Таким образом, с точки зрения пломорфизма (обратите внимание, что приведенный выше код не является полиморфным), вы бы сделали что-то вроде

public class SpecialClass
{
  public virtual XElement SerializeToXML()
  {
    // Base impl
  }
}

public class YourDerivedClasses
{
  public override XElement SerializeToXML()
  {
    //derived implementation 
  }
}

Довольно прямо. Но у вас проблема не в сериализации или полиморфном поведении, а в создании экземпляра правильного типа из XML.

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

Обратите внимание, ваша фабрика также может быть статическим методом в Базовом классе.

Что-то вроде (конечно, не проверено) ....

public class SpecialClass
{
  ***snip
  public static IEnumerable<SpecialClass> GetAllClasses(XElement xml)
  {
    IDictionary keyedtypes = GetKeyedTypesDictUsingReflection() // the registered factories
    foreach(var x in xml.Elements("YourClassesNode"))
    {
      string key = //get key from xml
      yield return keyedTypes[key].DeserializeFromXML(x);
    }
  }
}
0 голосов
/ 25 ноября 2010

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

SerializeToXML() принимает базовый класс SpecialClass в качестве параметра, поэтому он не долженНе различают производные классы ReadSpecialClass и WriteSpecialClass.Почему у вас нет SerializeToXML() в качестве метода экземпляра базового класса?

0 голосов
/ 25 ноября 2010
if (sc is WriteSpecialClass)
{
    sc = (WriteSpecialClass) sc;
}
 else if (sc is ReadSpecialClass)
{
    sc = (ReadSpecialClass) sc;
}
else
{
    throw new NotSupportetException("Type not Supportet");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...