Интерфейс IEnumerable классов, реализующих данный интерфейс во время выполнения


Я реализации шаблона команды в проекте, в котором я работаю и у меня есть интерфейс, ICommandFactory , что все мои команды исполнителей.

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

Поэтому я придумал способ ниже. Это была первая вещь, которую я мог приступить к работе. Есть ли лучший способ сделать это?

    private static IEnumerable<ICommandFactory> GetAvailableCommands()
    {
        var interfaceType = typeof (ICommandFactory);
        var implementors = Assembly.GetExecutingAssembly().GetTypes().Where(
            i => interfaceType.IsAssignableFrom(i) && i != interfaceType);

        var commands = new List<ICommandFactory>();

        foreach(var implementor in implementors)
        {
            var ctor = implementor.GetConstructor(Type.EmptyTypes);
            if (ctor == null) continue;
            var instance = ctor.Invoke(null) as ICommandFactory;
            if (instance == null) continue;
            commands.Add(instance);
        }

        return commands;
    }


863
6
задан 22 февраля 2011 в 08:02 Источник Поделиться
Комментарии
3 ответа

Ваш код, кажется, делать то, что он должен делать.

Я беспокоюсь о том, что ты намекаешь на возможность иметь несколько CommandFactory классов. Зачем тебе это? (Я просто спрашиваю. Вы можете иметь очень веские причины.)

Я также интересно, если вы на самом деле означало завода, так как имя переменной, которую вы используете для списка внутри ваш метод-это просто команды, которая заставляет меня верить, что вы ищете именно это: типы, реализующие метод ICommand, не ICommandFactory.

2
ответ дан 22 февраля 2011 в 10:02 Источник Поделиться

У меня есть только несколько незначительных замечаний о вашем коде:


  1. Этот код:

    var ctor = implementor.GetConstructor(Type.EmptyTypes);
    if (ctor == null) continue;

    имеет потенциал, чтобы замаскировать ошибку. Представьте, что вы добавить конструктор к одному из вашей команды-заводские типы и случайно забыть, что это неявно удаляет конструктор по умолчанию. Этот код будет молча глотать эту ошибку и просто не списка что конкретной команды завода. Было бы предпочтительнее бросить в этом случае, так что вы сразу заметите его. Еще лучше, конечно, было бы использовать событие после построения, чтобы превратить эту ошибку в Проверка времени компиляции , так что вы можете даже не запускать программу, когда он является недействительным.


  2. Этот код:

    var instance = ctor.Invoke(null) as ICommandFactory;
    if (instance == null) continue;

    и точно такая же проблема, но последствия гораздо более тонким. В случае, если (экземпляр == null), то условие должно не огонь, потому что код был написан так, что экземпляр не может быть любого другого типа. Представьте, что у вас есть ошибка в этом коде. Этот код проглатывает эту ошибку и вы не заметите его сразу. Вы только заметили некоторые тонкие/странные эффекты, которые возникают значительно позже, и это будет боль, чтобы выследить его код здесь. Вы могли бы сделать это бросить, как и выше, но лично я думаю, что вы должны изменить это ВАР экземпляр = (ICommandFactory) конструктор.Вызов(значение null); так что он будет автоматически выбрасывать, если это не правильный тип.


  3. Ваш код не позволяет для класса, который реализует ICommandFactory , но не должны отображаться в пользовательском интерфейсе. Если вы уверены, что вы не хотите, чтобы это было возможно, то ваш код-это нормально. В противном случае, я бы объявить пользовательский атрибут — либо один, что позволяет явно исключают в ICommandFactory появление в пользовательском интерфейсе, или тот, который требуется по всем ICommandFactory типы, которые должны быть включены.

  4. Очень мелкие придираться:

    i => interfaceType.IsAssignableFrom(i) && i != interfaceType

    Вы должны выполнить проще проверить:

    i => i != interfaceType && interfaceType.IsAssignableFrom(i)

5
ответ дан 23 февраля 2011 в 05:02 Источник Поделиться

Вы можете использовать активатор для создания экземпляра:

commands.Add( Activator.CreateInstance(implementor) as ICommandFactory );

или даже фантазии составлен Lamdba выражение.

0
ответ дан 23 февраля 2011 в 02:02 Источник Поделиться