简体   繁体   中英

C#, reflection, inheritence and static fields?

I have a family of classes that inherit from an abstract superclass, which is implemented by two concrete classes:

public abstract class AbstractFoo 
{
    protected static string fooName = "Reset me!";

    public static string GetName()
    {
         return fooName;
    }
}

The subclasses are then constructed like

public class BarFoo : AbstractFoo 
{
    static BarFoo() 
    {
         fooName = "Pretty Name For BarFoo";
    }
}

and so forth.

I want to get a list of all the AbstractFoo implementations' pretty names so the user can decide which implementation to use.

My reflection code looks like

 Type fooType = typeof(AbstractFoo);

 List<Assembly> assemblies = new List<Assembly>(AppDomain.CurrentDomain.GetAssemblies());

 IEnumerable<Type> allTypes = assemblies.SelectMany<Assembly, Type>(s => s.GetTypes());
 IEnumerable<Type> fooTypes = allTypes.Where(p => p.IsSubclassOf (fooType));

 foreach (Type thisType in fooTypes) 
 {
      MethodInfo method = thisType.GetMethod ("GetName", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
      string name = (string) method.Invoke (null, null);
  // add to the list, anyhow names.Add (name);  
  }

I end up with method.Invoke always returning "Rename Me" rather than the individual names.

I'm pretty sure I'm doing something silly here, but I'm not quite sure what.

You have two problems.

First, your static field really isn't going to be doing what you want it to. There's one static field, in AbstractFoo - there isn't a separate BarFoo.fooName static field. So if you have a bunch of subclasses, whichever subclass gets type-initialized last will "win" in setting the field.

Next, when you invoke BarFoo.GetName , that's really just a call to AbstractFoo.GetName - BarFoo won't get initialized, so you won't see the "pretty name" being set.

Fundamentally, I suggest you redesign your code. I recommend that you decorate each class with an attribute. That way you won't end up relying on the type initializer at all, and you don't need to declare a separate static member for each type. The only downside is that the value has to be a constant...

An alternative is to use a virtual property which is then overridden in subclasses - although that requires you to create an instance of each type, of course.

There is one copy of the static member. With your current setup, every subclass will overwrite that one copy, resulting in just one name being available. You need to make a static GetName function on every subclass and just return the name directly. I would recommend something along the lines of:

public abstract class AbstractFoo
{
}

public class BarFoo : AbstractFoo 
{
    public static string GetName()
    {
        return "Pretty Name For BarFoo";
    }
}

 Type fooType = typeof(AbstractFoo);
 List<Assembly> assemblies = new List<Assembly>(AppDomain.CurrentDomain.GetAssemblies());
 IEnumerable<Type> allTypes = assemblies.SelectMany<Assembly, Type>(s => s.GetTypes());
 IEnumerable<Type> fooTypes = allTypes.Where(p => p.IsSubclassOf (fooType));
 foreach (Type thisType in fooTypes) 
 {

      MethodInfo method = thisType.GetMethod ("GetName", BindingFlags.Public | BindingFlags.Static);
      string name = (string) method.Invoke (null, null);
  // add to the list, anyhow names.Add (name);  
  }

Another way to do it would be to keep a Dictionary as a static member in AbstractFoo and have the subclass' static initializers add something to that dictionary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM