Открытые общие типы интерфейсов открытой реализации не равны типу интерфейса?

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

[TestMethod]
public void can_get_open_generic_interface_off_of_implementor()
{
    typeof(OpenGenericWithOpenService<>).GetInterfaces().First()
        .ShouldEqual(typeof(IGenericService<>));
}
public interface IGenericService<T> { }
public class OpenGenericWithOpenService<T> : IGenericService<T> { }
  • Почему это не проходит?
  • Учитывая Type t = typeof(OpenGenericWithOpenService<>), как мне получить typeof (IGenericService < > )?

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

Ответ 1

OpenGenericWithOpenService<T> не реализует только произвольное IGenericService<> - он реализует IGenericService<T> для того же T как класс.

Лучший способ показать это - слегка изменить класс:

public class OpenGenericWithOpenService<T1, T2> : IGenericService<T1> {}

Теперь важно, что, когда вы спрашиваете, что для интерфейсов, которые он реализует, вы знаете, что можете преобразовать в IGenericService<T1>, но (совпадение в сторону), а не IGenericService<T2> или любую другую реализацию.

Другими словами, он не полностью открыт - он привязан к аргументу того же типа, что и у класса.

Я никогда не был очень хорош с терминологией дженериков, но надеюсь, вы понимаете, что я имею в виду. IGenericService<> - тип, ожидающий ввода аргумента типа; в этом случае у вас есть аргумент типа - это просто другой параметр типа!

Здесь пройдет тест, который пройдет:

[TestMethod]
public void can_get_open_generic_interface_off_of_implementor()
{
    Type[] typeParams = typeof(OpenGenericWithOpenService<>).GetGenericArguments();
    Type constructed = typeof(IGenericService<>).MakeGenericType(typeParams);
    typeof(OpenGenericWithOpenService<>).GetInterfaces().First()            
        .ShouldEqual(constructed);
}

Если вы измените класс для реализации (скажем) IGenericService<int> вместо этого, он будет терпеть неудачу.