2013-07-30 4 views
8

Контекст: .NET 4.0, C#C# Использование дженериков и интерфейс реализации

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

упрощенный пример это один:

namespace TestGenerics 
{ 
    // Interface, of fields 
    interface IField 
    { 
    } 

    // Interface: Forms (contains fields) 
    interface IForm<T> where T : IField 
    { 

    } 

    // CONCRETE CLASES 
    class Field : IField 
    { 
    } 

    class Form <T> : IForm<T> where T : IField 
    { 
    } 

    // TEST PROGRAM 
    class Program 
    { 
     // THIS IS THE SIGNATURE OF THE METHOD I WANT TO CALL 
     // parameters are causing the error. 
     public static void TestMethod(IForm<IField> form) 
     { 
      int i = 1; 
      i = i * 5; 
     } 

     static void Main(string[] args) 
     { 
      Form<Field> b = new Form<Field>(); 
      Program.TestMethod(b); 
     } 
    } 
} 

код имеет смысл для меня, но я получаю ошибку компиляции:

Argument 1: cannot convert from ' TestGenerics.Form<TestGenerics.Field> ' to ' TestGenerics.IForm<TestGenerics.IField> ' TestGenerics

я не уверен, что я делаю неправильно , Я прочитал много страниц в Интернете, но никто не решил мою проблему.

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

Edit: Я разработал интерфейсы таким образом, чтобы они должны быть независимыми от конкретных clases, которые реализуют их , Бетонные кладки могут быть загружены из dll, но большая часть приложения работает с интерфейсами. В некоторых случаях мне нужно использовать конкретные кланы, особенно при использовании класов, которые необходимо сериализовать.

Заранее спасибо.

Алехандро

+5

Это подпадает под сферу ковариации. В частности, если вы добавите модификатор ['out' generic modifier] (http://msdn.microsoft.com/en-us/library/dd469487.aspx) к вашему сигнатурному интерфейсу IForm , где T: IField', то он будет работать ,Но это добавляет другие ограничения/соображения, поэтому я не могу комментировать, применим ли это для вашего текущего дизайна/использования. –

+2

Мне любопытно, что именно вы пытаетесь выполнить с помощью этого дизайна. – sidesinger

+0

Большое спасибо за обоих поклонников, они были действительно шутливыми. Я добавил редактирование, объясняющее, почему я решил решение таким образом, но оба андерса могут исправить мою проблему. С уважением. – Sugar

ответ

13

Проблема заключается в том, что Form<Field> реализует IForm<Field> но не IForm<IField>. Вы не можете использовать унаследованный класс (или интерфейс) в качестве общего параметра, если он не помечен как ковариантный с идентификатором out. Тем не менее, маркировка вашего интерфейса как ковариантного будет значительно ограничивать использование (в основном, в интерфейсе «только для вывода», например IEnumerable), поэтому он может не сработать для вас.

Один из способов заставить его работать, чтобы сделать TestMethod родовые, а также:

public static void TestMethod<T>(IForm<T> form) where T:IField 
{ 
    int i = 1; 
    i = i * 5; 
} 
10

Вы можете использовать ковариационный, например, так:

interface IForm<out T> where T : IField 
{ 

} 

Подробнее о Ковариационном и контрвариации here.

+1

Ковариантность, когда вы позволяете использовать производный класс в качестве параметра общего типа с использованием ключевого слова «out». В приведенной ссылке вы можете увидеть это предложение: ", потому что параметр типа интерфейса IEnumerable является ковариантным, вы можете назначить экземпляр IEnumerable (IEnumerable (Of Derived) в Visual Basic) переменной типа IEnumerable " –

7

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

Вы уже сказали, что используете методы, объявленные в интерфейсе IField, поэтому нет необходимости создавать общий класс IForm - просто им нужно хранить ссылки на IField вместо общего аргумента «T» (который уже гарантированно является IFFI).

Например, используйте:

public interface IForm 
{ 
    IEnumerable<IField> Fields { get; set; } 
} 

вместо

public interface IForm<T> where T : IField 
{ 
    IEnumerable<T> Fields { get; set; } 
} 
+0

Согласовано. Другим признаком является то, что потребительский код создавал это событие как IForm и Form , что указывает на достаточность IField. Истинный тест зависит от того, какой конкретный тип T должен быть частью внешнего API. – Tormod

Смежные вопросы