C # виртуальные (или абстрактные) статические методы - PullRequest
12 голосов
/ 18 апреля 2009

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

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, и он бы сделал эту работу. Но есть ли способ получить такой же эффект?

Редактировать: Да, я мог бы поместить копию Operation в дочерний класс, но для этого потребовалось бы скопировать и вставить большой фрагмент кода в каждый дочерний класс, что в моем случае составляет около 35 классов, - кошмар обслуживания. *

Ответы [ 4 ]

12 голосов
/ 18 апреля 2009

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

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

8 голосов
/ 18 апреля 2009

Вы можете сохранить 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 Вы можете или не можете заботиться об этом. Если вы это сделаете, дженерики или Dictionary<Type, Action> могут помочь.

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

2 голосов
/ 16 апреля 2012

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

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;
    }
}

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

2 голосов
/ 18 апреля 2009

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

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 как решение, потому что использование наследования экземпляров, вероятно, лучшая рекомендация для любого в подобной ситуации. Но мне просто пришлось бы переписать слишком много кода для этого.

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