【问题标题】:C# virtual (or abstract) static methodsC# 虚拟(或抽象)静态方法
【发布时间】:2009-04-18 12:16:37
【问题描述】:

静态继承就像实例继承一样工作。除非您不允许将静态方法设为虚拟或抽象。

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 个类,这是维护的噩梦。

【问题讨论】:

标签: c# inheritance


【解决方案1】:

不,您不能覆盖静态方法。 “静态”也意味着它是由编译器静态绑定的,所以实际调用的方法不是在运行时找到的,而是在编译时绑定的。

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

【讨论】:

    【解决方案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。你可能关心也可能不关心。如果你这样做了,泛型或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 原则的情况下想要做的事情。

        【讨论】:

          猜你喜欢
          • 2016-12-14
          • 2010-11-17
          • 2014-12-24
          • 2016-01-16
          • 2011-03-19
          • 1970-01-01
          • 2011-07-18
          • 2012-10-11
          • 1970-01-01
          相关资源
          最近更新 更多