代码之家  ›  专栏  ›  技术社区  ›  Nick Whaley

C#虚拟(或抽象)静态方法

  •  12
  • Nick Whaley  · 技术社区  · 17 年前

    静态继承的工作原理与实例继承类似。除了不允许将静态方法设置为虚拟或抽象。

    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设置为虚拟的,它就能完成这项工作。但是,有没有一种变通方法可以达到同样的效果呢?

    4 回复  |  直到 17 年前
        1
  •  13
  •   Stefan Steinegger    17 年前

    不,您不能重写静态方法。“静态”也意味着它是由编译器静态绑定的,因此要调用的实际方法不是在运行时找到的,而是在编译时绑定的。

    你应该做的是使类非静态。使方法虚拟化并覆盖它,充分利用真正的继承。然后,如果你真的需要它,可以创建一个静态入口点,指向你的类的引用。例如,静态工厂、单例(在大多数情况下是一种反模式,但与静态类一样好)或只是一个静态属性。

        2
  •  9
  •   Mark Brackett    17 年前

    您可以将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>

    不过,总的来说,如果你不坚持静态,或者使用组合而不是继承,你会过得容易得多。

        3
  •  2
  •   Nick Whaley    17 年前

    如果你想做抽象静态方法,那么这是可行的,事实证明这是我最容易适应的解决方案:

    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
  •  2
  •   Stephen Kennedy annamataws    9 年前

    好的,这就是我所做的

    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原则的情况下实现你的目标。