How can I access a static property in a subclass when the property is located in a base class?

╄→гoц情女王★ 提交于 2019-12-24 01:54:57

问题


Let's say I have:

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");
    }

}

If I just do this in the calling code:

foreach(String supplier in Banana.Suppliers)
    Console.WriteLine(supplier);

I get:

  • Company A

Whereas if I do:

Banana b = new Banana();
foreach(String supplier in Banana.Suppliers)
    Console.WriteLine(supplier);

I get (the desired result):

  • Company A
  • Company B

Edit: After reading the responses I understand that this won't work.

What I want in my production code is a list of values that is common to the type of object and I want to dynamically add different values to that list of strings based on the subtype. (The context is LDAP - all entries have objectClass=top and all user-objects have objectClass=user,top,organizationPerson,person). Guess I have to use an interface or different lists in each subclass or something if no one has a better suggestion?


回答1:


For one thing, accessing Banana.Suppliers is misleading. It will always yield the same result as accessing Apple.Suppliers etc - you've got a single collection of suppliers.

Basically any time you access Banana.Suppliers the compiler emits a call to Fruit.Suppliers: that's why just calling Banana.Suppliers isn't triggering the static constructor which adds the banana supplier.

The reason you only see the suppliers added in the static constructor for bananas after you've created a banana is that that forces the static constructor to run. You could do anything else that forced the static initializer to run, and you'd get the same results. One example would be calling a static method within Banana itself.

Now, I strongly suspect that you've got a significant problem in that you'll be using the same suppliers for all types. Obviously this isn't your real code, and the best solution will depend on what you want your real code to do. Generics can give you effectively "per type" static variables using type arguments : Foo<Banana>.StaticProperty and Foo<Apple>.StaticProperty will really be different, assuming StaticProperty is declared in Foo<T>.

EDIT: With regards to your edit, I would suggest avoiding using statics here. Possibly create a factory for each type (implementing an interface which may be generic). Note that you may well be able to avoid creating a separate factory type for each subtype, if you can create an appropriate instance with all the relevant items for each type.

We'd really need to see more of an example to say for sure, but in general I find that the less static data you have, the more testable your design will be and the less you'll run into problems like this :)




回答2:


The results you are seeing are caused by the way that static constructors work. The CLR does not actually execute the static constructor util the first instance is used, which is why you only get the desired results in your second example. See MSDN for more info.




回答3:


The cuase of this is quite easily explainable, in fact. When you get Banana.Suppliers, you're actually just referencing Fruit.Suppliers - the compiler ends up resolving to the Fruit class in this case because of the way inheritance works (nothing is defined in the Banana class. Thus, your static constructor for Banana is not called in the first example because you have technically not yet referenced the class in any way. This is of course why you are missing the "Company B" item in the first result.

The problem here is a design issue. I'm not sure exactly what your intentions are here, but if you do in fact want a property in the Fruit class to store the list of all suppliers, then you need to initialise the list completely within the static constructor for the Fruit class. In general, however, I would think you'd want a separate dataset class or such for this purpose. Static properties is probably not the way to approach this design feature.




回答4:


Accessing Banana.Suppliers gets compiled to an access of Fruit.Suppliers ... which means that effectively your code is not actually touching the Banana class, which means that .NET has no reason to execute the static constructor of Banana.

If you did pretty much anything else with the Banana class (such as you creating an instance of it for example), the Banana static constructor runs.




回答5:


A static constructor does not behave like an instance constructor (they're not called explicitly). You have to access a property that's actually on the Banana class before it is constructed. You're trying to apply some principles of object orientation to static behavior on classes. They're not equatable, and doing this is going to lead you down a route will ultimately lead you to despair.

This code:

foreach(String supplier in Banana.Suppliers)
    Console.WriteLine(supplier);

Is the exact equivalent to this code:

foreach(String supplier in Fruit.Suppliers)
    Console.WriteLine(supplier);

So the static constructor on Banana is never called, because it is never needed. The below code demonstrates how making a call to a static member of fruit causes its static constructor to be called, yielding the results you're looking for.

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);


来源:https://stackoverflow.com/questions/995930/how-can-i-access-a-static-property-in-a-subclass-when-the-property-is-located-in

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!