2013-07-17 2 views
0

У меня есть семейство классов, которые наследуют от абстрактного суперкласса, который реализуется с помощью двух конкретных классов:C#, отражение, наследование и статические поля?

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

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

Подклассы затем сконструированный как

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

и так далее.

Я хочу получить список всех красивых имен AbstractFoo реализаций, чтобы пользователь мог решить, какую реализацию использовать.

Мой код выглядит как отражение

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

Я в конечном итоге с method.Invoke всегда возвращаются «Переименовать Me», а не отдельные имена.

Я уверен, что я делаю что-то глупое здесь, но я не совсем уверен, что.

+0

Поля и методы 'static' на самом деле не« унаследованы ». Они являются глобальными (-ish) функциями/переменными, пространство имен которых является охватывающим классом, и это пространство имен автоматически доступно в дочерних классах. – millimoose

+0

Справедливо. Я полностью готов переработать, как я храню/получаю имя, пока я могу получить это поведение (проверяя все реализующие классы и собираю список своих «хороших имен».) – Alterscape

ответ

4

У вас есть две проблемы.

Во-первых, ваше статическое поле действительно не будет делать то, что вы хотите. Есть одно статическое поле, в AbstractFoo - нет отдельного BarFoo.fooName статического поля. Поэтому, если у вас есть куча подклассов, какой бы подкласс не получил инициализацию типа, последний будет «выигрывать» при настройке поля.

Далее, при вызове BarFoo.GetName, что это на самом деле просто призыв к AbstractFoo.GetName - BarFoo не инициализируются, поэтому вы не увидите «красивое имя» быть установлен.

В принципе, предлагаю вам перепроектировать ваш код. Я рекомендую вам украсить каждый класс атрибутом. Таким образом, вы не будете полностью полагаться на инициализатор типа, и вам не нужно объявлять отдельный статический член для каждого типа. Единственным недостатком является то, что значение должно быть постоянным ...

Альтернативой является использование виртуального свойства, которое затем переопределяется в подклассах, хотя для этого требуется создать экземпляр каждого типа, конечно.

+0

Jon, учитывая, что он реализует ваши предложения и использует виртуальная собственность, должен ли его код отражения поднять экземпляр через Activator и вызвать в экземпляре? То есть, чтобы позволить runtime делать свой алгоритм разрешения? –

+0

@GarryVass: Да, это потребуется - что вполне может быть идеальным. Мы не знаем, какова цель на данный момент. Я сильно подозреваю, что атрибуты будут лучше подходят. –

+0

+1 для стратегии украшения –

2

Существует одна копия статического элемента. При вашей текущей настройке каждый подкласс будет перезаписывать одну копию, в результате чего будет доступно только одно имя. Вам нужно создать статическую функцию GetName для каждого подкласса и просто вернуть имя напрямую. Я бы рекомендовал что-то вдоль линий:

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

Другой способ сделать это будет держать Dictionary как статический член в AbstractFoo и имеют подкласс статические инициализаторы добавить что-то к этому словарю.