C# 当静态属性位于基类中时,如何访问子类中的静态属性?

C# 当静态属性位于基类中时,如何访问子类中的静态属性?,c#,inheritance,C#,Inheritance,假设我有: public class Fruit { public static List<String> Suppliers { get; protected set; } static Fruit() { Suppliers = new List<String>(); Suppliers.Add("Company A"); } } public class Banana : Fruit {

假设我有:

public class Fruit
{

    public static List<String> Suppliers { get; protected set; }

    static Fruit()
    {
        Suppliers = new List<String>();
        Suppliers.Add("Company A");
    }

}

public class Banana : Fruit
{

    static Banana()
    {
        Suppliers.Add("Company B");
    }

}
我得到:

  • A公司
如果我这样做:

Banana b = new Banana();
foreach(String supplier in Banana.Suppliers)
    Console.WriteLine(supplier);
我得到(期望的结果):

  • A公司
  • B公司
编辑: 在阅读了回复后,我明白这是行不通的


在我的生产代码中,我想要的是一个对象类型通用的值列表,并且我想要根据子类型动态地向该字符串列表添加不同的值。(上下文是LDAP-所有条目的objectClass=top,所有用户对象的objectClass=user、top、organizationPerson、person)。如果没有人有更好的建议,我想我必须在每个子类中使用一个接口或不同的列表或其他什么?

您看到的结果是由静态构造函数的工作方式造成的。在使用第一个实例时,CLR实际上并不执行静态构造函数,这就是为什么在第二个示例中只得到所需的结果。有关更多信息,请参阅。

首先,访问
Banana.Suppliers
会产生误导。它总是会产生与访问苹果供应商相同的结果-你有一个单一的供应商集合

基本上,每当您访问
Banana.Suppliers
时,编译器都会发出对
Fruit.Suppliers
的调用:这就是为什么只调用
Banana.Suppliers
并不会触发添加香蕉供应商的静态构造函数

创建香蕉后,您只能在香蕉的静态构造函数中看到添加的供应商,这是因为这会强制静态构造函数运行。您可以执行任何其他强制静态初始值设定项运行的操作,您将得到相同的结果。一个例子是在
Banana
本身中调用静态方法

现在,我强烈怀疑你有一个重大的问题,你将使用相同的供应商为所有类型。显然,这不是您真正的代码,最好的解决方案将取决于您希望真正的代码做什么。泛型可以使用类型参数有效地为您提供“每类型”静态变量:
Foo.StaticProperty
Foo.StaticProperty
将确实不同,假设
StaticProperty
Foo
中声明

编辑:关于你的编辑,我建议避免在这里使用静态。可能为每种类型创建一个工厂(实现一个可能是泛型的接口)。请注意,如果可以为每个子类型创建一个包含所有相关项的适当实例,则可以避免为每个子类型创建单独的factory类型


我们确实需要看更多的例子来确定,但一般来说,我发现静态数据越少,设计的可测试性就越高,遇到这样的问题也就越少:)

事实上,这种情况很容易解释。当您得到
Banana.Suppliers
时,实际上您只是引用了
Fruit.Suppliers
——由于继承的工作方式,编译器最终解析为
Fruit
类(在
Banana
类中没有定义任何内容。因此,在第一个示例中没有调用
Banana
的静态构造函数,因为您在技术上还没有以任何方式引用该类。这当然是您在第一个结果中缺少“Company B”项的原因


这里的问题是一个设计问题。我不确定您的意图到底是什么,但如果您确实希望
Fruit
类中的一个属性来存储所有供应商的列表,那么您需要在
Fruit
类的静态构造函数中完全初始化该列表。但是,一般来说,我认为您应该为此需要一个单独的数据集类或类似的类。静态属性可能不是实现此设计功能的方法。

访问
Banana。供应商
被编译为访问
Fruit.Suppliers
…这意味着您的代码实际上没有触及
Banana
类,这意味着NET没有理由执行香蕉的静态构造函数


如果您对
Banana
类执行了几乎所有其他操作(例如,您创建了它的实例),那么
Banana
静态构造函数将运行。

静态构造函数的行为与实例构造函数不同(它们没有显式调用)。在构建香蕉类之前,您必须访问香蕉类上实际存在的属性。您试图将一些面向对象的原则应用于类上的静态行为。这些原则是不平等的,这样做会使您走上一条最终会让您绝望的路

此代码:

foreach(String supplier in Banana.Suppliers)
    Console.WriteLine(supplier);
完全等同于此代码:

foreach(String supplier in Fruit.Suppliers)
    Console.WriteLine(supplier);
因此,香蕉上的静态构造函数永远不会被调用,因为它从来都不需要。下面的代码演示了调用fruit的静态成员是如何导致调用其静态构造函数的,从而产生您想要的结果

public class Banana : Fruit
{
    static Banana()
    {
        Suppliers.Add("Company B");
    }
    public static void Foo()
    {

    }
}

// ...
Banana.Foo();
foreach (var supplier in Banana.Suppliers)
    Console.WriteLine(supplier);

事实上,正如Jon Skeet已经指出的那样,这不会像我预期的那样起作用,因为只有一个静态的水果列表。我想我不是唯一一个有点困惑的人…;-)是的,我花了太长的时间来撰写我的答案。但是我留下了它,因为底部的示例演示了另一种强制调用静态构造函数的方法。请参阅我的编辑以了解生产代码环境的解释。更有意义…关于如何实现这一点,有什么想法吗?是我Jon,我误读了,或者是你编辑的maybee,但是起初我不认为你提到这与他没有触发Bannana上的静态构造函数有关。不管怎么说,现在已经很清楚了,所以我+1@Josh:Righto-谢谢你的解释:)(这很可能是一个编辑。)@antirysm:我回家后会看看。你应该为我提出一个新问题
public class Banana : Fruit
{
    static Banana()
    {
        Suppliers.Add("Company B");
    }
    public static void Foo()
    {

    }
}

// ...
Banana.Foo();
foreach (var supplier in Banana.Suppliers)
    Console.WriteLine(supplier);