C# с использованием дженериков и реализации интерфейса

контекст: .NET 4.0, C#

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

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

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);
        }
    }
}

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

3 ответов


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

один из способов заставить его работать-это сделать TestMethod generic, а также:

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

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

interface IForm<out T> where T : IField
{

}

подробнее о ковариации и Контравариации здесь.


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

вы уже сказали, что используете методы, объявленные в интерфейсе IField, поэтому, возможно, нет необходимости делать ваш класс iform универсальным - просто сохраните ссылки на IField вместо универсального аргумента " T " (который уже гарантированно будет IField в любом случае).

для экземпляр, используйте:

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

вместо

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