Ограничить универсальный тип в методе любым классом, производным от любой вариантной формы абстрактного базового класса. - PullRequest
0 голосов
/ 26 октября 2018

Итак, я искал и ищу решение этой проблемы, и мне больно известно, что, возможно, я просто не знаю, как правильно задать вопрос, чтобы найти ответ, поэтому яболее чем счастлив, если есть существующее решение, чтобы указать на соответствующую статью (или даже просто чтобы лучше понять / понять, как сказать, что именно я пытаюсь выяснить!)

При этом у меня есть абстрактный базовый класс для управления / обработки внешних, основанных на 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), от не распознавания запрошенных методов до попытки вызова методов-прототипов вместо переопределяющих ...

Это убивает меня уже три дня, и совершенно очевидно, что здесь происходит что-то, чего я не понимаю, поэтому любые указания, идеи, предложения и / или разъяснения будут с благодарностью!

1 Ответ

0 голосов
/ 26 октября 2018

Я не получил то, что вы действительно хотите сделать.Но, читая ваш код, я внес некоторые изменения, которые могут помочь:

  1. Добавить ограничение к PrototypeDataGroup:

    internal abstract class PrototypeDataGroup<T> where T : PrototypeDataClass, new()

  2. Добавить ограничение к методу DisplayContents:

    DisplayContents<Y,T>(string title) where Y : PrototypeDataGroup<T>, new() where T: PrototypeDataClass, new()

  3. Выполнить вызов метода DisplayContents, как показано ниже:

    base.DisplayContents<MySpecificDataGroup, MySpecificData>(title);

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...