【问题标题】:C#: How do I call a static method of a base class from a static method of a derived class?C#:如何从派生类的静态方法调用基类的静态方法?
【发布时间】:2010-10-10 19:56:51
【问题描述】:

在 C# 中,我有基类 Product 和派生类 Widget。

Product 包含一个静态方法 MyMethod()。

我想从静态方法 Widget.MyMethod() 调用静态方法 Product.MyMethod()。

我不能使用 base 关键字,因为它只适用于实例方法。

我可以显式调用 Product.MyMethod(),但如果我稍后将 Widget 更改为从另一个类派生,我必须修改该方法。

C# 中是否有一些类似于 base 的语法允许我从派生类的静态方法调用基类的静态方法?

【问题讨论】:

  • 这个特定方法是静态的有什么原因吗?
  • 解决问题的最佳方法是重命名Widget.MyMethod。首先考虑Widget.MyMethod 和Product.MyMethod 是否正在做相同的事情以使它们的名称相同。如果它们是等效的(对用户来说意味着相似的事情),那么您唯一的选择就是使它们成为非静态方法。 . 虽然我认为它会很有用,但 C# 没有静态成员继承的概念。现在想想FancyWidget 的孙子班,FancyWidget.MyMethod 应该叫什么?如果您认为 Widget.MyMethod 则 C# 没有这方面的东西(我认为任何 OOP 语言也没有)。

标签: c# inheritance static base derived-class


【解决方案1】:

可以做到:

public class Parent1
{
    protected static void Foo()
    {
        Console.WriteLine("Parent1");
    }
}

public class Child : Parent1
{
    public static void Foo()
    {
        return Parent1.Foo();
    }
}

可用于单元测试受保护的静态方法(例如)。

【讨论】:

    【解决方案2】:

    首先,如果您担心重新设置类的父级,那么您可能做错了继承。继承应该用于建立“is-a”关系,而不是简单地促进代码重用。如果您需要单独重用代码,请考虑使用委托,而不是继承。我想你可以在子类型和它的父类型之间引入一个中间类型,但我会让这种可能性驱动我的设计。

    其次,如果您需要使用基类中的功能但扩展它并且用例调用静态方法,那么您可能需要考虑使用一些外部类来保存该功能。在我看来,这方面的经典案例是工厂模式。实现工厂模式的一种方法是通过工厂方法,这是一个类的静态方法,用于构造该类的实例。通常构造函数是受保护的,因此工厂方法是从外部构建类的唯一方法。

    在继承层次结构中重用工厂方法的一种方法是将公共代码放在受保护的方法中并从工厂方法调用该方法,而不是直接从子类型调用基类工厂方法工厂方法。更好的实现可能使用相同的技术,但将工厂方法移动到工厂类并使用构造函数逻辑(现在是内部的,而不是私有的),可能与初始化方法结合使用来创建对象。如果您继承的行为是类外部的(解密/验证/等),您可以在 Factory 中使用共享方法(或组合)以允许在 Factory 方法之间重用。

    在不知道您使用静态方法的目标的情况下,很难给您一个准确的方向,但希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      这很简单。比使用别名、反射等简单得多。也许在 .NET、IDK 的新添加中它变得更容易,但这非常好。与实例方法一样,访问基类方法不需要使用base,它是可选的,通常在继承和基类具有同名方法时需要。即使没有base 关键字,您也可以访问基类的静态方法,就好像它们在您调用的类中一样。我只在从派生类的 static 方法调用基 static 方法时测试了这一点。如果您从 instance 方法调用静态方法,可能无法正常工作。

      public class BaseThings
      {
          protected static void AssertPermissions()
          {
              //something
          }
      }
      
      public class Person:BaseThings
      {
          public static void ValidatePerson(Person person)
          {
              //just call the base static method as if it were in this class.
              AssertPermissions();            
          }
      }
      

      【讨论】:

      • Aaron,OP 对于基类和派生类具有相同的方法名称。除了她不想明确指定Base.StaticMethod,这两个是问题的症结所在。
      • @nawfal Yeh 看起来好像没有他们说“来自静态方法同名”。那将是一个更好的标题。我确实看到他们提到了from static method Widget.MyMethod().,它恰好与基本方法同名。这是代码示例使事情更清晰的地方。我会用他们的例子来解决问题。我将保留我的答案,因为这出现在谷歌中,适用于调用基类静态方法的问题的标题。
      【解决方案4】:

      使用反射调用静态方法与调用实例方法完全相同,只是您为实例传递了 null。您需要 FlattenHierarchy,因为它是在祖先中定义的。

      var type = assy.GetType("MyNamespace.MyType");
      MethodInfo mi = type.GetMethod("MyStaticMethod", 
        BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
      mi.Invoke(null, null);
      

      进一步的阅读和思考让我提出了与其他回答相同的问题:为什么要使用这样的静态方法?您是否尝试进行函数式编程,如果是,为什么不使用 lambda 表达式呢?如果您想要具有共享状态的多态行为,实例方法会更好。

      【讨论】:

        【解决方案5】:

        静态方法是“类级”方法。它们旨在成为适用于特定类的所有实例的方法。因此,此方法的继承没有意义,因为它会改变应用于类实例的含义。例如,如果您正在循环通过一组产品(一些是 Widget,一些不是)并在每个产品上调用 MyMethod,那么被调用的方法将由类的实例确定。这违反了静态方法的目的。

        您可以通过根本不使用静态方法来更干净地执行您想要的操作,因为在您的示例中,MyMethod 似乎并不适用于 Product 的所有实例。但是,您可以通过使用像“IMyMethod”这样的接口类来实现您所描述的相同效果。这种方法仍然没有使用静态方法。我想我没有看到需要静态方法。为什么要使用静态方法开始?

        【讨论】:

          【解决方案6】:

          考虑到静态方法不应该在实例数据中中继...您应该有一个静态“MyMethod”,它根据参数或类似的东西表现不同。

          请记住,在一个类中定义的静态方法只是对代码进行排序的一种方式......但如果将该方法放在其他地方也是一样的......因为它不会中继放置它的对象.

          编辑:我认为您最好的选择是明确使用 Product.MyMethod...如果您认为...您的 Widget 更改其基类的可能性不大...而且在这种情况下,它只是代码的微小变化。

          【讨论】:

          • 不完全正确。静态方法可以依赖于一个类型(静态和实例)的私有成员,因此它们不是完全可移动的。
          • 是的。您知道静态变量(每个类一个)和实例变量(每个实例一个)之间的区别。为什么这种区别没有用?为什么是糟糕的 OO 设计?
          • 好吧,我应该说你“不应该”不中继实例变量数据......当然你“可以”。 :)
          【解决方案7】:

          可以,但不推荐。

          public class Parent1
          {
              public static void Foo()
              {
                  Console.WriteLine("Parent1");
              }
          }
          
          public class Child : Parent1
          {
              public new static void Foo()
              {
                  Type parent = typeof(Child).BaseType;
                  MethodInfo[] methods = parent.GetMethods();
                  MethodInfo foo = methods.First(m => m.Name == "Foo");
                  foo.Invoke(null, null);
              }
          }
          

          【讨论】:

          • 很公平。在这种情况下,解决方案比问题更糟糕。谢谢。
          • 你可以使用 MethodBase.GetCurrentMethod().Name 而不是使用字符串
          【解决方案8】:

          静态方法不是多态的,所以你想做的事情是不可能的。

          试图找到一种将静态方法视为多态的方法是可能的,但很危险,因为语言本身不支持它。

          一些建议:

          【讨论】:

            【解决方案9】:

            static 方法基本上是一种从面向对象概念中回退的方法。因此,它们在继承层次结构中不是很灵活,不可能直接做这样的事情。

            我能想到的最接近的东西是using 指令。

            using mybaseclass = Namespace.BaseClass;
            
            class MyClass : mybaseclass {
            
              static void MyMethod() {  mybaseclass.BaseStaticMethod();  }
            
            }
            

            【讨论】:

            • > 静态方法基本上是一种回退方法...为什么会这样?在我的类的代码中,依赖实例数据的方法是实例方法。不依赖实例数据而只依赖参数(如果有的话)的方法是静态方法。我发现这种区别很有用。
            • 不依赖于 object 实例的方法不符合为对象定义的大多数规则(虚拟方法调度,...)。
            • mindmodel,虽然它有一些场景,但如果你开始需要继承或[插入任何其他限制与非静态方法],这可能是其他东西可能关闭的症状......也许班级职责太多
            • @Freddy,正是我的观点。 几乎总是当你试图做一些语言不支持的事情时,你要么使用了错误的语言,要么试图把一个方钉放在一个圆孔里。 Ruby 会让@mindmodel 更接近他想要的,因为类是对象。
            • 我不同意静态方法不是面向对象的。它们可能经常以这种方式使用,但正如俗话说的“你可以用任何语言编写 Fortran”。在最坏的情况下,这些概念是正交的。请参阅我对一个经典案例的回答,其中静态方法直接用于支持面向对象的范式,即工厂模式。
            猜你喜欢
            • 2012-05-24
            • 2015-04-08
            • 2012-06-25
            • 2011-03-05
            • 2010-09-22
            • 2014-07-31
            • 2011-01-17
            • 2013-04-24
            相关资源
            最近更新 更多