Итак, я искал и ищу решение этой проблемы, и мне больно известно, что, возможно, я просто не знаю, как правильно задать вопрос, чтобы найти ответ, поэтому яболее чем счастлив, если есть существующее решение, чтобы указать на соответствующую статью (или даже просто чтобы лучше понять / понять, как сказать, что именно я пытаюсь выяснить!)
При этом у меня есть абстрактный базовый класс для управления / обработки внешних, основанных на XML исходных данных универсальными способами и для того, чтобы выступать в качестве основы для тонны производных классов, которые лежат на вершине и контекстуализируют эти необработанные данные для использования-специфичные форматы.
В другом классе, предназначенном для того, чтобы стать абстрактной основой для ряда других классов, чья работа заключается в управлении данными, которые хранятся в первом наборе классов, которые я описал.Во втором фундаментальном классе есть метод, в который я хочу передавать все возможные классы, производные от моего абстрактного базового класса данных, и при этом метод не имеет никакого предвидения того, чем на самом деле является входящий класс (кромедолжен быть получен из вышеупомянутых классов данных архетипов).
Это, очевидно, довольно запутанно, и это трудно понять и объяснить словами (таким образом, моя проблема - попытаться задать правильный вопрос, чтобы найти ответ)ниже приведен (сильно) урезанный пример кода, который, я надеюсь, мог бы лучше проиллюстрировать то, что я пытаюсь сказать ...
internal abstract class PrototypeDataClass
{
// intention is to hold and manage one generic record of unknown data.
protected List<KeyValuePair<string, string>> _data = new List<KeyValuePair<string,string>>();
protected PrototypeDataClass(PrototypeDataClass source) =>
this._data.AddRange(source._data.ToArray());
// Expects an XmlNode containing field data...
protected PrototypeDataClass(XmlNode source)
{
XmlNodeCollection nodes = source.GetElementsByTagName("field");
foreach (XmlNode node in nodes)
{
string key = XmlNode.Attributes["field"].Value,
value = XmlNode.InnerText;
this.Add(key,value);
}
}
public int Count => this._data.Count;
public string this[string index]
{
get {
int i = FindFieldByName(index);
if ((i<0) || (i>=Count)) throw new ArgumentOutOfRangeException();
return this[i];
}
set => this.Add(index,value);
}
protected int FindFieldByName(string fieldname)
{
int i=-1; while ((++i < Count) && !_data[i].Key.Equals(fieldname, StringComparison.InvariantCultureIgnoreCase));
return (i < Count) ? i : -1;
}
public void Add(string key, string value) =>
Add(new KeyValuePair<string,string>(key, value));
public void Add(KeyValuePair newData)
{
int i = FindFieldByName(newData.Key);
if (i<0)
this._data.Add(newData);
else
this._data[i] = newData;
}
public abstract string FormattedDisplayLine();
public static bool IsDerivedType(dynamic test) =>
IsDerivedType(test.GetType());
public static bool IsDerivedType(Type test) =>
(test == typeof(Object)) || (test is null) ? false :
(test.BaseType == typeof(PrototypeDataClass)) ? true : IsDerivedType(test.BaseType);
}
// Problem 1: I would like the WHERE constraint here to facilitate using
// only derivatives of PrototypeDataClass for T...
internal abstract class PrototypeDataGroup<T> where T : new()
{
List<T> _data = new List<T>();
protected PrototypeDataGroup()
{
// A clunky workaround to validate that the supplied generic type is
// derived from my prototype...
if (!PrototypeDataClass.IsDerivedType(typeof(T)))
throw new Exception(typeof(T).Name + " is not derived from PrototypeDataClass.");
}
protected PrototypeDataGroup(T[] sourceData)
{
// Same clunky workaround...
if (!PrototypeDataClass.IsDerivedType(typeof(T)))
throw new Exception(typeof(T).Name + " is not derived from PrototypeDataClass.");
foreach(T line in sourceData)
this.Add(line);
}
protected PrototypeDataGroup(XmlDocument doc)
{
// Same clunky workaround...
if (!PrototypeDataClass.IsDerivedType(typeof(T)))
throw new Exception(typeof(T).Name + " is not derived from PrototypeDataClass.");
XmlNodeCollection nodes = doc.GetElementsByTagName("rows");
foreach (XmlNode node in nodes)
this._data.Add(new PrototypeDataClass(node));
}
public int Count => this._data.Count;
public T this[int index] => this._data[index];
public void Add(T item) =>
this._data.Add(item);
public abstract string[] FormattedDisplayLines();
}
internal class MySpecificData : PrototypeDataClass
{
public MySpecificData() : base() { }
public MySpecificData(PrototypeDataClass source) : base(source) { }
public MySpecificData(XmlNode source) : base(source) { }
public MySpecificData(KeyValuePair data) : base() =>
this.Add(data);
public MySpecificData(string key, string value) : base() =>
this.Add(key, value);
// Code to manage / present the generic data in MySpecific ways...
public override string FormattedDisplayLine() =>
_data["specificField1"] + ": " + _data["specificField2"];
}
internal class MySpecificDataGroup : PrototypeDataGroup<MySpecificData>
{
public MySpecificDataGroup() : base() { }
public MySpecificDataGroup(XmlDocument doc) : base(doc) { }
public MySpecificDataGroup(MySpecificData[] source) : base(source) { }
// present / manage the collection in MySpecific ways
public override string[] FormattedDisplayLines()
{
List<string> lines = new List<string>();
for(int i=0; i<Count; i++)
lines.Add(new MySpecificData(this._data[i]).FormattedDisplayLine());
return lines.ToArray();
}
}
// This class's purpose is to provide the foundation for another set of
// classes that are designed to perform work using the data held in various
// derivations of PrototypeDataGroup<T>
internal abstract class SomeOtherClassFoundation
{
XmlDocument _doc;
public SomeOtherClassFoundation(XmlDocument source) =>
this._doc = source;
// Problem 2: would like to simply constrain Y here to EVERY/ANY
// possible derivation of PrototypeDataGroup, but when I try that,
// i.e. "public void DisplayDoc<Y>(string title) where Y : PrototypeDataGroup, new()"
// the compiler spits out an error that appears to demand that I
// pre-declare every individual allowable "Y" variant separately:
// "Using the generic type 'PrototypeDataGroup<T>' requires at least 1 type arguments"
// Soo: "public void DisplayDoc<Y>(string title) where Y : PrototypeDataGroup<MySpecificDataGroup>, PrototypeDataGroup<MyOtherSpecificDataGroup>, new()"
// As there could ultimately be dozens of such derived classes, having
// to maintain such a list manually is beyond daunting and seems
// absurd. Is there no way to specify:
// "where Y : PrototypeDataGroup<>, new()" (for any/all values of '<>'?)
protected void DisplayContents<Y>(string title) where Y : new()
{
// If I use "Y" here in lieu of "dynamic", the code won't even
// compile as the compiler decides that it's absolutely impossible for
// the Y type to have either the "Count" or "FormattedDisplayLines" methods.
// By using "dynamic", it at least waits until runtime to make that evaluation
// then reacts accordingly (though usually still badly)...
dynamic work = new Y();
if (work.Count > 0)
{
Console.WriteLn("Displaying " + work.Count.ToString() + " records:\r\n"+ title);
foreach (string line in work.FormattedDisplayLines())
Console.WriteLn(line);
}
}
}
internal class SomeOtherClassForMySpecificData : SomeOtherClassFoundation
{
public SomeOtherClassForMySpecificData(XmlDocument source) : base(source) { }
public Show()
{
string title = "Specific Field 1 | Specific Field 2\r\n".PadRight(80,'=');
base.DisplayContents<MySpecificData>(title);
}
}
Итак, в дополнение к вещам, которые я упомянул вВ комментариях выше компилятор также отклоняет этот вызов base.DisplayContents<MySpecificData>(title);
с ошибкой:
Ошибка CS0310 «MySpecificData» должен быть неабстрактного типа с открытым конструктором без параметров, чтобы использовать его в качестве параметра'Y' в универсальном типе или методе 'SomeOtherClassFoundation.DisplayContents (string) '
Очевидно, MySpecificData
имеет открытый конструктор без параметров и, хотя он ПРОИЗВОДИТСЯ из абстрактного базового типа, сам по себе он не один ...
Кроме того, существует множество проблем с динамической реализацией производных классов в функции DisplayContents(string)
, от не распознавания запрошенных методов до попытки вызова методов-прототипов вместо переопределяющих ...
Это убивает меня уже три дня, и совершенно очевидно, что здесь происходит что-то, чего я не понимаю, поэтому любые указания, идеи, предложения и / или разъяснения будут с благодарностью!