C#: 如何从派生类的静态方法中调用基类的静态方法?

21
在C#中,我有一个基类Product和派生类Widget。
Product包含一个静态方法MyMethod()。
我想从静态方法Widget.MyMethod()调用静态方法Product.MyMethod()。
我不能使用base关键字,因为它仅适用于实例方法。
我可以显式地调用Product.MyMethod(),但如果以后将Widget更改为派生自另一个类,则必须修改该方法。
在C#中是否有类似于base的语法,允许我从派生类的静态方法中调用基类的静态方法?

1
这个特定的方法为什么是静态的? - Rad
问题的最佳解决方案是重命名 Widget.MyMethod。首先要考虑一下 Widget.MyMethodProduct.MyMethod 是否在做相同的事情,以便它们的名称相同。如果它们是等效的(对用户来说意思相似),那么你唯一的选择就是将它们变成非静态方法。虽然我认为这很有用,但 C# 没有静态成员继承的概念。现在想象一下 FancyWidget 的孙子类,FancyWidget.MyMethod 应该调用什么?如果你认为应该调用 Widget.MyMethod,那么 C# 没有这样的东西(我认为任何面向对象编程语言都没有)。 - nawfal
9个回答

21

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

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

using mybaseclass = Namespace.BaseClass;

class MyClass : mybaseclass {

  static void MyMethod() {  mybaseclass.BaseStaticMethod();  }

}

4
静态方法基本上是一种回退方法(fallback method),只需使用类名即可调用,而不需要创建类的实例。通常,静态方法不依赖于类的特定实例数据,而是依赖于传递给它们的参数。在您的类代码中,依赖于实例数据的方法是实例方法,而不依赖于实例数据但仅依赖于参数(如果有)的方法是静态方法。这种区分对您很有用。 - MindModel
不依赖于对象实例的方法不遵循为对象定义的大多数规则(虚拟方法分派等)。 - Mehrdad Afshari
mindmodel在某些情况下很有用,但如果你开始需要继承或者其他限制与非静态方法相对立的情况,那可能意味着还有其他问题存在...也许这个类承担了过多的责任。 - eglasius
@Freddy,我的想法完全一样。当您尝试做一些语言不支持的事情时,几乎总是如此,那么您要么使用了错误的语言,要么尝试将方形木块放入圆孔中。由于类是对象,因此Ruby可以让 @mindmodel 更接近他想要的东西。 - Michael Meadows
2
我不同意静态方法不是面向对象的说法。虽然它们经常被这样使用,但正如俗话所说,“你可以用任何语言编写Fortran”。最坏的情况下,这些概念是正交的。请参阅我的答案,其中经典案例之一是静态方法直接用于支持面向对象范式,即工厂模式。 - tvanfosson

4
这是可以做到的,但我不建议这么做。
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);
    }
}

7
好的,针对这个情况,解决办法比问题本身更糟糕。谢谢。 - MindModel
3
您可以使用 MethodBase.GetCurrentMethod().Name 替代 string,这将更为简洁明了。 - Sergey Mirvoda

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表达式呢?如果你想要具有共享状态的多态行为,实例方法将更好。

3
可以实现:
public class Parent1
{
    protected static void Foo()
    {
        Console.WriteLine("Parent1");
    }
}

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

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


2
首先,如果你担心重新父类化一个类,那么你可能在错误地使用继承。继承应该用于建立“is-a”关系,而不仅仅是促进代码重用。如果你只需要代码重用,考虑使用委托而不是继承。我想你可以在子类型和其父类之间引入一个中间类型,但我会让这种可能性推动我的设计。
其次,如果你需要使用基类的功能来扩展它,并且用例调用静态方法,则可能要考虑使用一些外部类来保存功能。我心目中的经典案例是工厂模式。实现工厂模式的一种方式是通过工厂方法,在类上创建一个静态方法来构造该类的实例。通常构造函数是受保护的,以便工厂方法是从外部构建类的唯一方法。
在继承层次结构中使用工厂方法进行重用的一种方法是将公共代码放在受保护的方法中,并从工厂方法中调用该方法,而不是直接从子类型的工厂方法中调用基类的工厂方法。更好的实现可能会使用相同的技术,但将工厂方法移动到工厂类中,并使用构造函数逻辑(现在是内部的,而不是私有的),也许结合初始化方法,来创建对象。如果你继承的行为是外部类(解密/验证等),你可以在工厂中使用共享方法(或组合)来允许工厂方法之间的重用。
不知道你使用静态方法的目的,很难给你一个确切的方向,但希望这能有所帮助。

1

静态方法不具有多态性,因此您想要做的是不可能的。

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

一些建议:


0

考虑到静态方法不应该依赖实例数据,所以您应该拥有一个基于参数或类似东西不同行为的静态“MyMethod”。

请记住,在一个类中定义的静态方法只是整理代码的一种方式……但如果该方法放在其他地方,它仍然是一样的,因为它不依赖于它所放置的对象。

编辑: 我认为您最好明确使用Product.MyMethod...... 如果您认为这样做...... 您的Widget更改其基类的可能性不大...... 在那种情况下,这只是一种较小的代码更改。


不完全正确。静态方法可以依赖于类型的私有成员(包括静态和实例),因此它们并不是完全可移动的。 - Mehrdad Afshari
真的。您知道静态变量(每个类一个)和实例变量(每个实例一个)之间的区别。为什么这种区别没有用?为什么它是不好的面向对象设计? - MindModel
嗯,我应该说你“不应该”依赖于实例变量数据……当然你“可以”。 :) - Romias

0

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

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


0

非常简单。比使用别名、反射等要简单得多。也许在较新版本的.NET中,这个过程已经变得更加容易了,我不确定,但这样做完全没有问题。与实例方法一样,访问基本方法并不需要使用base关键字,它是可选的,通常在继承和基类具有相同名称的方法时必须使用。即使没有base关键字,您也可以像调用类中的方法一样访问基类的静态方法。我只测试了从派生类的< strong >static< /strong >方法调用基础< strong >static< /strong >方法的情况。如果从实例方法调用静态方法,则可能不起作用。

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
@nawfal 看起来他们没有说“从相同名称的静态方法”这个地方。那将是一个更好的标题。我确实看到他们提到了from static method Widget.MyMethod(),这恰好与基本方法的名称相同。这就是代码示例使事情更清晰的地方。我会使用他们的示例来解决问题。我会保留我的答案,因为这在谷歌上出现,并适用于调用基类静态方法的问题标题。 - AaronLS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接