Реализация методов перегрузки для подтипов абстрактного класса в C# - PullRequest
2 голосов
/ 13 января 2020

Я новичок в C#, и я действительно хотел бы реализовать определенные c различные методы для каждого подтипа определенного абстрактного класса, но у меня возникли проблемы с выяснением, как заставить компилятор сделать это правильно , Например:

public abstract class MasterClass { }
public class SubClass1 : MasterClass { }
public class SubClass2 : MasterClass { }

public class SeparateClass
{
    public void HandleMasterClass(MasterClass item)
    {
       /*
       stuff generic to both subclasses...
       */
       SpecificMethod(item)
    }

    public void SpecificMethod(SubClass1 item)
    {
        //something specific to SubClass1
    }

    public void SpecificMethod(SubClass2 item)
    {
        //something specific to SubClass2
    }
}

Это возвращает ошибку при компиляции, потому что нет SpecificMethod(MasterClass item), но я действительно хочу, чтобы он выбрал правильный метод на основе подкласса без необходимости писать отдельный HandleMasterClass(SubClass1 item) и HandleMasterClass(SubClass2 item) методы, потому что они в основном один и тот же код

мой основной язык - Jula, поэтому я очень привык полагаться на множественную диспетчеризацию и делать подобные вещи. Я знаю, что это, вероятно, не idiomati c в C#, так как бы я сделал это лучше?

РЕДАКТИРОВАТЬ: показывая, что методы не являются бесплатными, но являются частью отдельного класса

здесь лучший конкретный пример

public abstract class MasterClass { public abstract int Stuff(); }
public class SubClass1 : MasterClass
{
    public override int Stuff() { /*calculate and return an int*/ }
}

public class SubClass2 : MasterClass
{
    public override int Stuff() { /*calculate and return an int*/ }
}

public class MasterClassDictionary
{
    public Dictionary<int, SubClass1> subClass1Dict{get;} = new Dictionary<int, SubClass1>()
    public Dictionary<int, SubClass2> subClass2Dict{get;} = new Dictionary<int, SubClass2>()

    public void Add(MasterClass item)
    {
        int val = item.Stuff();
        AddToDict(val, item);
    }

    void AddToDict(int val, SubClass1 item) { subClass1Dict[val] = item; }
    void AddToDict(int val, SubClass2 item) { subClass2Dict[val] = item; }

}

Я знаю, что это немного надуманный пример, но он похож на то, что я пытаюсь сделать.

Ответы [ 2 ]

3 голосов
/ 13 января 2020

Как правило, вы хотите поместить код, указанный c, в класс внутри этого класса. Таким образом, ваш абстрактный класс будет определять сигнатуру метода speci c, используя ключевое слово abstract, а реализация будет жить внутри класса, используя ключевое слово override, например это:

public abstract class MasterClass {
    public abstract void SpecificMethod();
}

public class SubClass1 : MasterClass {
    public override void SpecificMethod()
    {
        //something specific to SubClass1
        // use the this keyword to access the instance

    }
}

public class SubClass2 : MasterClass {
    public override void SpecificMethod()
    {
        //something specific to SubClass2
        // use the this keyword to access the instance
    }
}

public class SeparateClass
{
    public void HandleMasterClass(MasterClass item)
    {
      /*
      stuff generic to both subclasses...
      */
      item.SpecificMethod()
    }
}

Согласно вашему комментарию, я могу реализовать эту вещь в вашем конкретном примере, хотя она может не соответствовать вашим требованиям:

public class MasterClassDictionary
{
    public Dictionary<int, SubClass1> subClass1Dict{get;} = new Dictionary<int, SubClass1>()
    public Dictionary<int, SubClass2> subClass2Dict{get;} = new Dictionary<int, SubClass2>()

    public void Add(MasterClass item)
    {
        int val = item.Stuff();

        if (item is SubClass1)
        {
            subClass1Dict[val] = item;
        }

        if (item is SubClass2)
        {
            subClass2Dict[val] = item;
        }
    }
}
1 голос
/ 13 января 2020

Стандартный шаблон проектирования для этой ситуации - Шаблон посетителя . Это довольно сложный шаблон, но основная идея заключается в том, что подклассы знают, какого они типа, поэтому мы собираемся вызвать их через виртуальный метод, называемый «Принять», и они передадут себя обратно в качестве ссылки. Метод, который они вызывают, называется визитом и перегружен для всех возможных подклассов. Вот реализация для вашего примера:

public abstract class MasterClass
{
    public abstract int Stuff();

    // New method that all subclasses will have to implement.
    // You could also have this be virtual with an implementation
    // for Visit(MasterClass) to provider a default behavior.
    public abstract void Accept(IVisitor visitor);
}

public class SubClass1 : MasterClass
{
    public override int Stuff() => 0;

    // We must override this even though its the "same" code in both subclasses
    // because 'this' is a reference to a different type.
    public override void Accept(IVisitor visitor) => visitor.Visit(this);
}

public class SubClass2 : MasterClass
{
    public override int Stuff() => 1;

    // We must override this even though its the "same" code in both subclasses
    // because 'this' is a reference to a different type.
    public override void Accept(IVisitor visitor) => visitor.Visit(this);
}

public interface IVisitor
{
    // Need an overload for all subclasses.
    void Visit(SubClass1 item);
    void Visit(SubClass2 item);
}

public class MasterClassDictionary
{
    public Dictionary<SubClass1, int> subClass1Dict { get; } = new Dictionary<SubClass1, int>();
    public Dictionary<SubClass2, int> subClass2Dict { get; } = new Dictionary<SubClass2, int>();

    public void Add(MasterClass item)
    {
        int val = item.Stuff();
        var visitor = new Visitor(this, val);
        item.Accept(visitor);
    }

    void AddToDict(SubClass1 item, int val) { subClass1Dict[item] = val; }

    void AddToDict(SubClass2 item, int val) { subClass2Dict[item] = val; }

    // Provides the visitor implementation that holds any state that might
    // be needed and dispatches to the appropriate method.
    private class Visitor : IVisitor
    {
        private MasterClassDictionary _parent;
        private int _value;

        public Visitor(MasterClassDictionary parent, int val)
        {
            _parent = parent;
            _value = val;
        }

        public void Visit(SubClass1 item) => _parent.AddToDict(item, _value);

        public void Visit(SubClass2 item) => _parent.AddToDict(item, _value);
    }
}

Тем не менее, C# добавил сопоставление с переключателем, которое выглядело бы значительно проще. Единственным недостатком является то, что он делает больше проверок типов, которые могут быть медленнее, если это делается в каком-то действительно чувствительном к производительности коде, но, безусловно, будет быстрее, чем использование dynamic:

public void Add(MasterClass item)
{
    int val = item.Stuff();
    switch (item)
    {
        case SubClass1 i: AddToDict(i, val); break;
        case SubClass2 i: AddToDict(i, val); break;
    }
}
...