Подтвердить что ты не робот

Виртуальные (или абстрактные) статические методы С#

Статическое наследование работает так же, как наследование экземпляра. Кроме того, вам не разрешено делать статические методы виртуальными или абстрактными.

class Program {
    static void Main(string[] args) {
        TestBase.TargetMethod();
        TestChild.TargetMethod();
        TestBase.Operation();
        TestChild.Operation();
    }
}

class TestBase {
    public static void TargetMethod() {
        Console.WriteLine("Base class");
    }

    public static void Operation() {
        TargetMethod();
    }
}

class TestChild : TestBase {
    public static new void TargetMethod() {
        Console.WriteLine("Child class");
    }
}

Это выведет:

Base class
Child class
Base class
Base class

Но я хочу:

Base class
Child class
Base class
Child class

Если бы я мог использовать статические методы, я бы сделал TargetMethod виртуальным, и он выполнил бы эту работу. Но есть ли работа, чтобы получить тот же эффект?

Изменить: Да, я могу поместить копию Операции в дочерний класс, но для этого потребуется скопировать и вставить большой бит кода в каждый ребенок, который в моем случае составляет около 35 классов, кошмар обслуживания.

4b9b3361

Ответ 1

Нет, вы не можете переопределить статический метод. "static" также означает, что он статически связан компилятором, поэтому фактический метод, который должен быть вызван, не найден во время выполнения, но связан во время компиляции.

Что вам нужно сделать, так это сделать класс нестатическим. Сделайте метод виртуальным и переопределите его и в полной мере используйте реальное наследование. Затем, если вам это действительно нужно, сделайте статическую точку входа в ссылку вашего класса. Например, статический factory, singleton (это анти-шаблон в большинстве случаев, но не хуже статического класса) или просто статическое свойство.

Ответ 2

Вы можете сохранить TargetMethod в качестве делегата, который подкласс мог бы изменить по мере необходимости:

class TestBase {
    protected static Action _targetMethod;

    static new() {
       _targetMethod = new Action(() => {
           Console.WriteLine("Base class");
       });
    }

    public static void TargetMethod() {
        _targetMethod();
    }

    public static void Operation() {
        TargetMethod();
    }
}

class TestChild : TestBase {
    static new() {
       _targetMethod = new Action(() => {
           Console.WriteLine("Child class");
       });
    }
}

Так как это статические экземпляры, тем не менее - _targetMethod является общим для всех экземпляров - изменение его в TestChild также изменяет его на TestBase. Вы можете или не должны заботиться об этом. Если вы это сделаете, могут помочь generics или Dictionary<Type, Action>.

В целом, вам было бы намного легче, если бы вы не настаивали на статике или, возможно, использовали композицию вместо наследования.

Ответ 3

Если вы ищете абстрактные статические методы, то это работает и оказывается самым простым решением для меня:

class TestBase<ChildType> where ChildType : TestBase<ChildType> {
    //public static abstract void TargetMethod();

    public static void Operation() {
        typeof(ChildType).GetMethod("TargetMethod").Invoke(null, null);
    }
}

class TestChild : TestBase<TestChild> {
    public static void TargetMethod() {
        Console.WriteLine("Child class");
    }
}

Но я по-прежнему отмечаю Stafan как решение, потому что использование наследования экземпляров, вероятно, является лучшей рекомендацией для всех, кто находится в аналогичной ситуации. Но мне просто пришлось бы переписать слишком много кода для него.

Ответ 4

Хорошо, вот что я сделал

public abstract class Base<T>
    where T : Base<T>, new()
{
    #region Singleton Instance
    //This is to mimic static implementation of non instance specific methods
    private static object lockobj = new Object();
    private static T _Instance;
    public static T Instance
    {
        get
        {
            if (_Instance == null)
            {
                lock (lockobj)
                {
                    if (_Instance == null)
                    {
                        _Instance = new T();
                    }

                }
            }
            return _Instance;
        }
    }

    #endregion //Singleton Instance

    #region Abstract Definitions

    public abstract T GetByID(long id);
    public abstract T Fill(SqlDataReader sr);

    #endregion //Abstract Definitions
}

public class InstanceClass : Base<InstanceClass>
{
    //empty constructor to ensure you just get the method definitions without any
    //additional code executing
    public InstanceClass() { }


    #region Base Methods

    public override InstanceClass GetByID(long id)
    {
        SqlDataReader sr = DA.GetData("select * from table");
        return InstanceClass.Instance.Fill(sr);
    }

    internal override InstanceClass Fill(SqlDataReader sr)
    {
         InstanceClass returnVal = new InstanceClass();
         returnVal.property = sr["column1"];
         return returnVal;
    }
}

Я думаю, что это будет жизнеспособное решение для того, что вы хотите сделать, не нарушая слишком много пуристских принципов OO.