.NET 4.0 Общий инвариант, ковариант, контравариант

Вот сценарий, с которым я столкнулся:

public abstract class Record { } 

public abstract class TableRecord : Record { } 

public abstract class LookupTableRecord : TableRecord { } 

public sealed class UserRecord : LookupTableRecord { } 

public interface IDataAccessLayer<TRecord> 
    where TRecord : Record { } 

public interface ITableDataAccessLayer<TTableRecord> : IDataAccessLayer<TTableRecord> 
    where TTableRecord : TableRecord { } 

public interface ILookupTableDataAccessLayer<TLookupTableRecord> : ITableDataAccessLayer<TLookupTableRecord> 
    where TLookupTableRecord : LookupTableRecord { } 

public abstract class DataAccessLayer<TRecord> : IDataAccessLayer<TRecord> 
    where TRecord : Record, new() { } 

public abstract class TableDataAccessLayer<TTableRecord> : DataAccessLayer<TTableRecord>, ITableDataAccessLayer<TTableRecord> 
    where TTableRecord : TableRecord, new() { } 

public abstract class LookupTableDataAccessLayer<TLookupTableRecord> : TableDataAccessLayer<TLookupTableRecord>, ILookupTableDataAccessLayer<TLookupTableRecord> 
    where TLookupTableRecord : LookupTableRecord, new() { } 

public sealed class UserDataAccessLayer : LookupTableDataAccessLayer<UserRecord> { }

Теперь, когда я пытаюсь привести UserDataAccessLayer к его общему базовому типу ITableDataAccessLayer<TableRecord>, компилятор жалуется, что не может неявно преобразовать тип.

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

У меня есть следующий абстрактный класс:

public abstract class FileProcessor : IDisposable
{
    protected abstract ITableDataAccessLayer<TableRecord> CreateTableDataAccessLayer();
}

И пример конкретной реализации следующим образом:

public class UserFileProcessor : FileProcessor
{
            protected override ITableDataAccessLayer<TableRecord> CreateTableDataAccessLayer()
        {
            return new UserDataAccessLayer();
        }
}

вернуть новый UserDataAccessLayer(); вот на что жалуется компилятор.


person Community    schedule 30.04.2010    source источник
comment
Пожалуйста, опубликуйте короткую, но полную программу, которая демонстрирует проблему, таким образом вы также можете получить конкретные ответы.   -  person Lasse V. Karlsen    schedule 30.04.2010
comment
Привет, Лассе, я внес поправки в свой пост в соответствии с твоей рекомендацией. Любая помощь будет принята с благодарностью.   -  person Sameer Shariff    schedule 30.04.2010
comment
Я также хотел бы увидеть полное определение UserDataAccessLayer, все методы, но подождите, позвольте мне добавить ответ, и тогда вы увидите, поможет ли это.   -  person Lasse V. Karlsen    schedule 30.04.2010


Ответы (2)


Насколько я знаю, вам нужно указать ключевое слово out для интерфейса, если вы хотите использовать ковариацию.

http://geekswithblogs.net/NewThingsILearned/archive/2009/09/30/covariance-in-.net-4.0.aspx

person Snake    schedule 30.04.2010
comment
Я также попытался установить общие параметры в универсальном интерфейсе. Но затем компилятор выдал мне другую ошибку: Недопустимая дисперсия: параметр типа «TTableRecord» должен быть неизменно действительным на... - person Sameer Shariff; 30.04.2010
comment
Если я применяю ключевое слово out к универсальному параметру TTableRecord в универсальном объявлении интерфейса ITableDataAccessLayer, я получаю следующую ошибку: Invalid variance: Параметр типа «TTableRecord» должен быть контравариантно допустимым для «Csss.Data.DataAccessLayers.ITableDataAccessLayer‹TTableRecord›. Удалить(TTableRecord, bool)'. «TTableRecord» является ковариантным. - person Sameer Shariff; 30.04.2010

Проблема с ко- и контравариантностью заключается в том, что она накладывает довольно много ограничений на задействованные типы, поэтому она может быть применима не во всех случаях.

Мне удалось скомпилировать ваш код, внеся следующие изменения:

public interface IDataAccessLayer<out TRecord>
    where TRecord : Record { }

public interface ITableDataAccessLayer<out TTableRecord> : IDataAccessLayer<TTableRecord>
    where TTableRecord : TableRecord { }

Примечание:

  • добавлено для IDataAccessLayer и ITableDataAccessLayer

Это, однако, означает, что вы не ограничены использованием TTableRecord только в выходных позициях в этих типах, что означает:

  • тип для свойств только для чтения (не для записываемых свойств)
  • возвращаемый тип для методов
  • типы аргументов для методов

Вы не можете не использовать его для:

  • записываемые свойства
  • ref или параметры non-out/ref для методов

Так что, скорее всего, здесь нет никакого способа заставить ко- и контравариантность помочь вам.

person Community    schedule 30.04.2010
comment
Привет Лассе Не работает. Добавьте в ITableDataAccessLayer метод, который использует тип TTableRecord в качестве параметра или возвращаемого типа. - person Sameer Shariff; 30.04.2010
comment
Есть ли что-нибудь еще, что я могу сделать, чтобы понизить конкретный тип до базового универсального типа? - person Sameer Shariff; 30.04.2010
comment
Как я уже сказал, у него масса ограничений, я сомневаюсь, что вы можете делать то, что хотите. - person Lasse V. Karlsen; 30.04.2010