MethodInfo.Invoke автоматическая трансляция из класса в интерфейс не работает - PullRequest
1 голос
/ 30 декабря 2011

Я пытаюсь вызвать метод класса с помощью отражения.

public class A
{
    public string B(IWork WorkToDo)
    {
        return WorkToDo.ToString();
    }
}

Класс "Работа" наследуется от "IWork".

Type type = Type.GetType("A");

MethodInfo MI = type.GetMethod("B");

object X = MI.Invoke(Activator.CreateInstance(type), new object[] { new Work() });

В приведенной выше строке выдается ошибка аргумента .. "Ожидаемый IWork ... вместо Work .."

Если я напишу

object X = MI.Invoke(Activator.CreateInstance(type), new object[] { (new Work() as IWork) });

это работает.

Я хочу знать, почему он не может автоматически выводить и типизировать полиморфизм, или я все понял неправильно? Можем ли мы иметь специальный класс Binder для сантехники?

Пожалуйста, помогите.


EDIT

OK. Извините, ребята, за сокрытие настоящей проблемы. Это работает для выше ... также работает с Array [], но выдает ошибку в List <> ...

Итак, мой класс будет:

public class A
{
    public string B(List<IWork> WorkToDo)
    {
        return WorkToDo.ToString();
    }
}

Type type = Type.GetType("A");

MethodInfo MI = type.GetMethod("B");
List<Work> WL = new List<Work>(); WL.Add(new Work());
object X = MI.Invoke(Activator.CreateInstance(type), new object[] { WL });

Ошибка:

Объект типа 'System.Collections.Generic.List 1[Work]' cannot be converted to type 'System.Collections.Generic.List 1 [IWork]'.

1 Ответ

2 голосов
/ 30 декабря 2011

С вашим обновлением проблема становится очевидной.List<Work> не является List<IWork>.List<T> не является ковариантным, как дано

interface IWork { }
class Work : IWork { } 
class OtherWork : IWork { } 

Метод, принимающий List<IWork>, вполне может попытаться добавить new OtherWork(), и это было бы совершенно законно во время компиляции.Если для метода был передан List<Work>, добавление OtherWork было бы абсолютно недопустимым временем выполнения , и поэтому язык не позволяет вам передавать List<Work> там, где ожидается List<IWork>.

То, что вы, возможно, захотите сделать, - это метод accept IEnumerable<IWork>, который позволит вам передать ваш List<Work>.IEnumerable<T> является ковариантным (в C # 4), и это может быть потому, что он доступен только для чтения, не будет никаких записей или добавлений в последовательность.

Полный рабочий пример:

namespace Foo
{
    public class A
    {
        // note the change to the signature
        public string B(IEnumerable<IWork> workToDo)
        {
            return workToDo.ToString();
        }

        static void Main()
        {
            var type = Type.GetType("Foo.A");
            var info = type.GetMethod("B");
            var list = new List<Work>();
            var x = info.Invoke(Activator.CreateInstance(type), new [] { list });
        }
    }

    public interface IWork { }
    public class Work : IWork { }
}

Что касается того, почему это работает для массива, дисперсия массива нарушена.Это позволяет вещи во время компиляции, которые могут быть совершенно опасными во время выполнения.Та же проблема может возникнуть во время выполнения, если у вас есть метод, принимающий IWork[] array, и вы передаете Work[], компилятор позволит вам сделать это в этом случае.Однако было бы совершенно законно (во время компиляции) сказать array[0] = new OtherWork();, и вы получите исключение времени выполнения.

...