Поиск классов, реализующих интерфейс, использование отражения и создание их экземпляров в F # - PullRequest
0 голосов
/ 22 января 2020

Я пытаюсь добиться следующего:

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

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

type IBotCommands =

    abstract member Name: unit -> string
    abstract member Description: unit -> string
    abstract member Help: unit -> string
    abstract member Execute: MessageEventArgs -> string[] -> string

и код:

let t = typeof<IBotCommands>
t.Assembly.GetTypes()
 |> Seq.filter (fun x -> x.IsSubclassOf(t))
 |> Seq.iter (fun x ->
     (
         let i = Activator.CreateInstance(x) :> IBotCommands
         botCommands.[i.Name] <- i
     )
 )

Проблема, с которой я столкнулся, связана со строкой CreateInstance. CreateInstance возвращает тип obj , который не может быть приведен к IBotCommands.

У меня то же самое в C#, и он работает правильно, но версия C# использует динамику:

    public static IEnumerable<Type> FindClassSubclassOfType<T>()
    {
        var a = typeof(T)
                .Assembly.GetTypes()
                .Where(t => t.IsSubclassOf(typeof(T)))
                .Select(t => t);

        return a.ToList();
    }

    var types = ReflectionHelper.FindClassSubclassOfType<BotCommand>();
    foreach (var t in types)
    {
        dynamic c = Activator.CreateInstance(t);
        BotCommands[c.Name] = c;
    }

как мне заставить это поведение работать в F #?

можно ли привести объект к интерфейсу в F #? я впервые использую интерфейсы в F #

1 Ответ

3 голосов
/ 22 января 2020

В F # есть разница между восходящей a :> T и прямой a :?> T.

  • Upcast используется, когда компилятор статически знает, что a реализует интерфейс T. Это полезно, если у вас есть значение конкретного класса и вы хотите получить значение, имеющее тип интерфейса.

  • Downcast используется, когда компилятор не знает статически a реализует интерфейс. Другими словами, это означает, что приведение может завершиться неудачей.

В вашем случае вам нужен downcast, потому что компилятор не знает, реализует ли obj IBotInterface. Все, что вам нужно сделать, это добавить ?:

let i = Activator.CreateInstance(x) :?> IBotCommands
botCommands.[i.Name] <- i
...