2013-05-13 5 views
3

Учитывая следующий класс и интерфейс:Дженерики и проблема интерфейса

public class Test<T> 
{ 
} 

public interface ITesting<T> 
{ 
    Test<T> LoadTest(); 
} 

Я могу добавить класс со следующим:

public class TestManager : ITesting<object> 
{ 
    #region ITesting 

    public Test<object> LoadTest() 
    { 
     return new Test<object>(); 
    } 

    #endregion 
} 

Который работает нормально и не приводит к возникновению ошибок. Если я пытаюсь заменить этот класс следующим:

public class TestDerived : Test<object> 
{ 
} 

public class TestDerivedManager : ITesting<object> 
{ 
    #region ITesting 

    public TestDerived LoadTest() 
    { 
     return new TestDerived(); 
    } 

    #endregion 
} 

теперь я получаю следующее сообщение об ошибке:

 
Error 'TestDerivedManager' does not implement interface member 'ITesting.LoadTest()'. 'TestDerivedManager.LoadTest()' cannot implement 'ITesting.LoadTest()' because it does not have the matching return type of 'Test'. 

Но мне кажется, что это должно работать как TestDerived является Test<object>. Я, очевидно, не понимаю что-то правильно здесь. Может ли кто-нибудь указать мне, почему это неверно? И, возможно, что я могу сделать, чтобы исправить это?

+3

Я отмечаю, что это поведение не имеет ничего общего с дженериками. Если у вас есть интерфейс I {Animal M(); } класс C: I {public Giraffe M() {return new Giraffe(); }} 'вы получите ту же ошибку. –

ответ

6

функцию, которую вы хотите, называется тип возвращаемого ковариации. Это особенность C++, но не C#, поэтому вам придется делать то, что говорит ошибка.

Я рекомендую вам выполнить явную реализацию интерфейса, которая вызывает ваш общедоступный метод. Таким образом, вы получаете лучшее из обоих миров.

4

Измените возвращаемый тип метода на Test<object>, и он должен работать. Тип возврата является частью контракта при реализации интерфейса. Вы все равно можете вернуть TestDerived, так как это Test<object>.

+2

Это было бы, но я не думаю, что это объясняет, почему это не работает, когда TestDerived на самом деле Тест cgatian

6

TestDerived является Test<object>, да, но Test<object> является неTestDerived.

В контракте указано, что у ITesting<object> есть метод, который может возвращать любые Test<object>. Не только один специальный случай (TestDerived).

Чтобы сделать код работы, изменить его следующим образом:

public class TestDerivedManager : ITesting<object> 
{ 
    public Test<object> LoadTest() 
    { 
     return new TestDerived(); 
    } 
} 
3

То, что вы ищете, называется «return type covariance» и, к сожалению, не поддерживается C#. Вы не можете переопределить (или реализовать) метод с другим типом возвращаемого значения, чем исходная подпись, даже если этот тип возврата происходит из исходного типа возврата.

Что вы можете сделать (что может или не может быть хорошей идеей), это реализовать интерфейс в явном виде и имеют, что называют публичный метод, который возвращает более узкий тип, который вы ищете:

public class TestDerived : Test<object> 
{ 
} 

public class TestDerivedManager : ITesting<object> 
{ 
    public TestDerived LoadTest() 
    { 
    return new TestDerived(); 
    } 

    Test<object> ITesting<object>.LoadTest() 
    { 
    return this.LoadTest(); 
    } 
} 

Это добавляет дополнительный уровень сложности для вашей реализации, но также дает вам открытый контракт, который вы ищете, а также совместимость с интерфейсом.

0

У каждого есть хороший технический ответ, но никто не объясняет почему. Поэтому я думаю, что дам иллюстрацию об интерфейсе.

Общий способ программирования интерфейса - вы должны реализовать точный метод или свойство, описанные в интерфейсе.Давайте предположим, что вы имеете интерфейс:

public interface ITesting<T> 
{ 
    Test<T> LoadTest(); 
} 

Использование этого интерфейса в потребителе должен быть так:

ITesting<object> testingLoader = GetTestingLoader(); 
Test<object> testingLoader.LoadTest(); 

Если доступ к объекту с помощью интерфейса, вы не знаете, реализация. Вы только знаете, что интерфейс ITesting<T> вернет Test<T> для метода LoadTest().

Это будет иметь смысл, если вы увидите пример потребителя выше. Вы (и компилятор) не знаете (и не будете) знать, что интерфейс ITesting<object> имеет способ Test<object> LoadTest(), если вы его не объявите. И что произойдет, если вы попытаетесь использовать объект, который реализован ITesting<object>, но не имеет метода Test<object> LoadTest()? Это будет ошибка, не так ли?

Это следует за LSV principle.

+0

Erhm, нет, вы ошибаетесь. Говоря о LSP: я должен уметь обрабатывать объект 'TestDerivedManager' как' ITesting ', вызывать 'LoadTest' на нем и получать' Test '(как указывает интерфейс). Реализация возвращает 'TestDerived', который _is_ a' Test '. Так что все хорошо, нет нарушения LSP, и код должен работать. Нет ошибок. Только оговорка: некоторые языки поддерживают его, но не C#. – Virtlink

+0

О, я вижу, спасибо, что освободил меня – Fendy