Вызов универсального метода с аргументом типа, известным только во время выполнения

Редактировать:

Конечно, мой реальный код выглядит не совсем так. Я попытался написать полупсевдокод, чтобы сделать его более понятным для того, что я хотел сделать.

Похоже, что вместо этого все испортилось.

Итак, что я на самом деле хотел бы сделать это:

Method<Interface1>();
Method<Interface2>();
Method<Interface3>();
...

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

Сценарий выглядит так:

public void Method<T>() where T : class
{}
public void AnotherMethod()
{
    Assembly assembly = Assembly.GetExecutingAssembly();

    var interfaces = from i in assembly.GetTypes()
    where i.Namespace == "MyNamespace.Interface" // only interfaces stored here
    select i;

    foreach(var i in interfaces)
    {
        Method<i>(); // Get compile error here!
    }




Исходное сообщение:

Привет!

Я пытаюсь перебрать все интерфейсы в пространстве имен и отправить их в качестве аргументов универсальному методу, например так:

public void Method<T>() where T : class
{}
public void AnotherMethod()
{
    Assembly assembly = Assembly.GetExecutingAssembly();

    var interfaces = from i in assembly.GetTypes()
    where i.Namespace == "MyNamespace.Interface" // only interfaces stored here
    select i;

    foreach(var interface in interfaces)
    {
        Method<interface>(); // Get compile error here!
    }
}

Я получаю сообщение об ошибке "Ожидается имя типа, но найдено имя локальной переменной". Если я попробую

...
    foreach(var interface in interfaces)
    {
        Method<interface.MakeGenericType()>(); // Still get compile error here!
    }
}

Я получаю "Невозможно применить оператор"<к операндам типа "группа методов" и "System.Type". Есть идеи, как обойти эту проблему?

1 ответ

Решение

РЕДАКТИРОВАТЬ: Хорошо, время для короткой, но полной программы. Основной ответ, как и прежде:

  • Найдите "открытый" универсальный метод с Type.GetMethod
  • Сделайте это универсальным, используя MakeGenericMethod
  • Вызвать его с помощью вызова

Вот пример кода. Обратите внимание, что я изменил выражение запроса на точечную нотацию - нет смысла использовать выражение запроса, когда вы в основном получили предложение where.

using System;
using System.Linq;
using System.Reflection;

namespace Interfaces
{
    interface IFoo {}
    interface IBar {}
    interface IBaz {}
}

public class Test
{
    public static void CallMe<T>()
    {
        Console.WriteLine("typeof(T): {0}", typeof(T));
    }

    static void Main()
    {
        MethodInfo method = typeof(Test).GetMethod("CallMe");

        var types = typeof(Test).Assembly.GetTypes()
                                .Where(t => t.Namespace == "Interfaces");

        foreach (Type type in types)
        {
            MethodInfo genericMethod = method.MakeGenericMethod(type);
            genericMethod.Invoke(null, null); // No target, no arguments
        }
    }
}

Оригинальный ответ

Давайте оставим в стороне очевидные проблемы вызова переменной "interface" для начала.

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

Получите универсальный метод и вызовите для него MakeGenericMethod, а затем вызовите его.

Ваш тип интерфейса на самом деле является общим? Я спрашиваю, потому что вы вызываете MakeGenericType, но не передаете аргументы любого типа... Вы пытаетесь вызвать

Method<MyNamespace.Interface<string>>(); // (Or whatever instead of string)

или же

Method<MyNamespace.Interface>();

Если это последнее, вам нужен только вызов MakeGenericMethod, а не MakeGenericType.

Другие вопросы по тегам