让我说我有:
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");
}
}
如果我只是在调用代码中执行此操作:
foreach(String supplier in Banana.Suppliers)
Console.WriteLine(supplier);
我明白了:
而如果我这样做:
Banana b = new Banana();
foreach(String supplier in Banana.Suppliers)
Console.WriteLine(supplier);
我得到了(期望的结果):
修改 阅读完回复后,我明白这不起作用。
我在生产代码中想要的是一个对象类型共有的值列表,我想根据子类型动态地将不同的值添加到该字符串列表中。 (上下文是LDAP - 所有条目都有objectClass = top,所有用户对象都有objectClass = user,top,organizationPerson,person)。猜猜如果没有人有更好的建议,我必须在每个子类中使用一个接口或不同的列表?
答案 0 :(得分:13)
首先,访问Banana.Suppliers
会产生误导。它总会产生与访问Apple.Suppliers
等相同的结果 - 您有一个单个供应商集合。
基本上,只要您访问Banana.Suppliers
,编译器就会调用Fruit.Suppliers
:这就是为什么只调用Banana.Suppliers
不会触发添加香蕉供应商的静态构造函数。
您在创建香蕉后只看到香蕉静态构造函数中添加的供应商的原因是强制静态构造函数运行。你可以做任何其他迫使静态初始化程序运行的东西,你会得到相同的结果。一个例子是在Banana
本身内调用静态方法。
现在,我强烈怀疑您遇到了一个重大问题,即您将为所有类型使用相同的供应商。显然,这不是您的真正的代码,最佳解决方案将取决于您希望实际代码执行的操作。泛型可以使用类型参数为您提供有效的“每类型”静态变量:Foo<Banana>.StaticProperty
和Foo<Apple>.StaticProperty
将真正不同,假设在StaticProperty
中声明了Foo<T>
。
编辑:关于你的编辑,我建议避免在这里使用静态。可能为每种类型创建一个工厂(实现可能是通用的接口)。请注意,如果您可以为每种类型创建包含所有相关项目的相应实例,则可以避免为每个子类型创建单独的工厂类型。
我们确实需要看到更多可以肯定的例子,但总的来说,我发现你拥有的静态数据越少,你的设计就越可测试,你遇到的问题就越少:)
答案 1 :(得分:6)
您看到的结果是由静态构造函数的工作方式引起的。 CLR实际上并不执行使用第一个实例的静态构造函数util,这就是为什么你只在第二个例子中得到所需的结果。有关详细信息,请参阅MSDN。
答案 2 :(得分:2)
Banana.Suppliers
时,你实际上只是引用Fruit.Suppliers
- 在这种情况下编译器最终解析为Fruit
类,因为继承的工作方式(在{中没有定义) {1}}类。因此,在第一个例子中没有调用Banana
的静态构造函数,因为你在技术上还没有以任何方式引用该类。这当然是你错过了“公司B”的原因。第一个结果中的项目。
这里的问题是设计问题。我不确定你的意图到底是什么,但如果你确实想要Banana
类中的一个属性来存储所有供应商的列表,那么你需要初始化完全列在Fruit
类的静态构造函数中。但是,一般情况下,我认为您需要一个单独的数据集类或此类目的。静态属性可能不是接近此设计功能的方法。
答案 3 :(得分:1)
访问Banana.Suppliers
被编译为访问Fruit.Suppliers
...这意味着您的代码实际上并没有触及Banana
类,这意味着.NET没有理由执行Banana
的静态构造函数。
如果你使用Banana
类做了很多其他事情(例如你创建了它的实例),Banana
静态构造函数就会运行。
答案 4 :(得分:0)
静态构造函数的行为与实例构造函数不同(它们不是显式调用的)。在构造之前,您必须访问实际上在Banana类上的属性。您试图将一些面向对象的原则应用于类上的静态行为。他们不是平等的,这样做会导致你走上一条路,最终会让你感到绝望。
此代码:
foreach(String supplier in Banana.Suppliers)
Console.WriteLine(supplier);
与此代码完全等效:
foreach(String supplier in Fruit.Suppliers)
Console.WriteLine(supplier);
因此永远不会调用Banana上的静态构造函数,因为它永远不需要。下面的代码演示了如何调用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);