2017-01-15 9 views
1

Я наткнулся на случай, который не влияет на мой дизайн, но, скорее всего, повлияет на будущие изменения кода и производительности.C# - Автоматическая инициализация полей класса через конструктор (ручной и динамический)

Мой класс выглядит следующим образом:

public sealed class Commands { 
public interface ICommand { 
    string commandName { get; set; } 
} 

public ICommand Maintain; 
public ICommand Dispatch; 
public ICommand Memorize; 
//............// 

private class ReckonCommand : ICommand { 
    public string commandName { get; set; } 

    public ReckonCommand (string name) { 
     this.commandName = name; 
    } 
} 

public Commands() { 
    Maintain = new ReckonCommand ("Maintain") as ICommand; 
    Dispatch = new ReckonCommand ("Dispatch") as ICommand; 
    Memorize = new ReckonCommand ("Memorize") as ICommand; 
} 

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

Альтернативный динамический подход заключается в следующем:

public ICommand Maintain; 
public ICommand Dispatch; 
public ICommand Memorize; 
//............// 

private class ReckonCommand : ICommand { 
    public float commandName { get; set; } 
    //Stuff 

} 

public Commands() { 
    System.Reflection.FieldInfo[] _fields = GetType().GetFields(); 
    for (int i = 0; i < _fields.Length; i++) { 
     if (_fields [i].FieldType == typeof(ICommand)) { 
      _fields [i].SetValue (this, new ReckonCommand() as ICommand); 
     } 
    } 
} 

Однако класс команд, как ожидается, будет инициализирован много раз (игнорировать имена, если это мешает вам получить идею), поэтому производительность потеря в какой-то момент может быть эффективной.

Есть ли альтернатива этому конкретному дизайну, который я использую? Моя задача - автоматически инициализировать каждое поле ICommand внутри конструктора класса Commands. Ручной подход подвержен ошибкам, которые могут быть слишком запоздалыми при обнаружении.

ответ

0

Динамический подход является абсолютно приемлемым (мы используем его все время в широкомасштабных приложениях для решения проблем, как вы описали).

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

Что-то вдоль линий:

private static System.Collections.Generic.List<System.Reflection.FieldInfo> _fields; 

    static Commands() 
    { 
     _fields = new System.Collections.Generic.List<System.Reflection.FieldInfo>(); 

     var fields = typeof(Commands).GetFields(); 
     var commandType = typeof(ICommand); 

     foreach (var field in fields) 
     { 
      if (field.FieldType == commandType) 
      { 
       _fields.Add(field); 
      } 
     } 
    } 

    public Commands() 
    { 
     foreach (var field in _fields) 
     { 
      field.SetValue(this, new ReckonCommand() as ICommand); 
     } 
    } 
+0

Отличная идея, спасибо. Две вещи: i) var, как известно, немного медленнее, чем прямое объявление типа; это потому, что вы не знаете, использую ли я пространства System.Collections.Generic и System.Reflection или это прекрасно в этой ситуации? ii) Как только у вас есть список fieldInfo, метод SetValue так же быстро, как и ваше типичное присвоение значений? Я спрашиваю, потому что интерфейс ICommand также будет иметь метод Modify(). Должен ли я использовать метод SetValue или придумать обходной путь? Modify() будет использоваться гораздо чаще, чем инициализация класса Commands. Cheers –

+0

re var: var - это проблема, связанная с компиляцией, когда компилятор передает тип переменной из присваивания. Это не медленнее во время выполнения, чем явно набирать переменную. Причина, по которой мы его используем, состоит в том, чтобы свести к минимуму количество изменений, необходимых при изменении типа данных. –

+0

re SetValue: он будет немного быстрее и сделает приложение более понятным для использования прямого метода на интерфейсе, а не SetValue. Однако сначала вам нужно заполнить класс Commands действительными экземплярами объектов ICommand, что и делает данный цикл. После того, как у вас есть экземпляр, созданный командой или циклом, вы затем будете использовать методы на интерфейсе ICommand для изменения этого экземпляра (т. Е. Метода Modify). –