#c# #abstraction #covariance #code-duplication
#c# #абстракция #ковариация #дублирование кода
Вопрос:
У меня есть 2 независимых класса A и B, и у меня есть класс хранения, который управляет хранением объектов типа A и B.
Я пытаюсь абстрагировать код, который сохраняет A и B, однако я застрял из-за ковариации списка, которую я не смог назначить List<object> objList = new List<A>();
в следующем коде.
[DataContract]
public class A {
public int UID;
}
[DataContract]
public class B {
public int UID;
}
public class Storage {
public void Store(A a) {
List<A> aList = ReadA();
if (aList == null) {
aList = new List<A>();
}
aList.Add(a);
WriteNodes(aList);
}
public void StoreB(B b) {
List<B> bList = ReadB();
if (bList == null) {
bList = new List<B>();
}
bList.Add(b);
WriteNodes(bList);
}
public List<A> ReadA() {
//deserializes from aFileName and returns List<A>
}
public List<B> ReadB() {
//deserializes from bFileName adn returns List<B>
}
private static void WriteNodes<T>(List<T> nodeList) {
FileStream fs = new FileStream(aFileName, FileMode.Create);
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs);
DataContractSerializer ser =
new DataContractSerializer(typeof(List<T>));
ser.WriteObject(writer, nodeList);
writer.Close();
fs.Close();
}
}
Если вы посмотрите на методы StoreA и StoreB, они имеют общий шаблон, за исключением используемого типа. ReadA и ReadB не проблема, я мог бы просто взять тип в качестве другого параметра и создать единую функцию чтения.
Итак, возможно ли создать абстракцию для хранилища, чтобы я не получал методы StoreA и StoreB?
Ответ №1:
Как насчет:
public void Store<T>(T a) {
List<T> aList = Read<T>();
if (aList == null) {
aList = new List<T>();
}
aList.Add(a);
WriteNodes(aList);
}
public List<T> Read<T>() {
//Read a or b depend on T
}
Ответ №2:
Да, вы можете сделать это, введя общий интерфейс для элементов, которые нужно создать, и извлечь тип элемента в enum.
Хранение
public class Storage
{
public Storage()
{
// create it once on construction stage
// so you do not need to check for null each time in Sore()/Read()
this.AllItems = new List<IItem>();
}
public IList<IItem> AllItems { get; private set; }
public void Store<TItem>(TItem item)
where TItem: IItem
{
this.AllItems.Add(item);
}
public IEnumerable<IItem> Read(StorageItemType itemType)
{
return this.AllItems.Where(item => item.ItemType == itemType);
}
}
Абстрактный тип элемента хранилища (более общее решение):
// Item types
enum StorageItemType
{
A,
B
}
interface IItem
{
int UID { get; }
StorageItemType ItemType { get; }
}
public abstract class StorageItemBase: IItem
{
public int UID { get; private set; }
public abstract StorageItemType ItemType
}
public sealed class B : StorageItemBase
{
public override StorageItemType ItemType
{
get
{
return StorageItemType.B; // !!!
}
}
}
Комментарии:
1. @sII, но хранение происходит с файлом, и я десериализуюсь из файла (т.Е. Я храню объекты типа A в одном файле, а объекты типа B в другом файле). Похоже, что в вашем решении есть объекты в памяти. Поэтому для сохранения в нужный файл мне нужно иметь сопоставление типа объекта с именем файла A->fileA; B->fileB и в методе Store<T>() извлеките имя файла перед фактическим сохранением.
2. Это не проблема, просто сопоставьте usign
IDictionary<StoreItemType, string>
like<StoreItemType.A, "fileA.xml">
,<StoreItemType.B, "fileB.xml">
а затем usign IDictionary . containsKey разрешить имя файла —if(dictionary.Containskey(StoreItemType.A)) { fileName = dictionary[StoreItemType.A]; }
3. хорошо; понял; Но является ли это стандартным шаблоном для явного сохранения типа объекта (в данном случае StorageItemType) как части самого фактического объекта? Насколько это отличается от typeof() ? Не будет ли typeof предоставлять почти ту же информацию, что и StorageItemType ?
4. StorageItemType — это тип бизнес-элемента, а не тип ОБЪЕКТА, который возвращается object . GetType() так что это довольно обычный метод для инкапсуляции некоторого набора типов с помощью enum