2009-11-22 6 views
2

Вот сценарий я столкнулся с:Cast конкретный класс для общего базового интерфейса

public abstract class Record { } 

public abstract class TableRecord : Record { } 

public abstract class LookupTableRecord : TableRecord { } 

public sealed class UserRecord : 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> { } 

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 { } 

Теперь, когда я пытаюсь сделать следующий бросок, он не компилируется:

UserDataAccessLayer udal = new UserDataAccessLayer(); 
      ITableDataAccessLayer<TableRecord> itdal = (ITableDataAccessLayer<TableRecord>)udal; 

Однако, когда я делаю следующий бросок компилируется без ошибок во время выполнения:

UserDataAccessLayer udal = new UserDataAccessLayer(); 
      ITableDataAccessLayer<UserRecord> itdal = (ITableDataAccessLayer<UserRecord>)udal; 

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

Надеюсь, это наглядно и полезно, чтобы ответить на мой вопрос.

+0

Я думаю, что у вас есть опечатка - обе части кода компиляции/не компиляции равны. – weismat

+0

Привет. Извините, не форматировал html правильно. Спасибо что подметил это. Пожалуйста, просмотрите прибыль. Благодаря! –

+0

они все те же. –

ответ

5

Что вы пытаетесь сделать, поддерживается в .NET 4.0, но не в 3.5. Это называется generic covariance. Вместо этого вы можете создать не общий интерфейс, называемый ITableDataAccessLayer (используя тип Object, где бы вы ни использовали T), и обеспечить явную реализацию интерфейса. Это то, как многие общие типы в .NET обрабатывают его.

+0

Привет, Джош, спасибо за ответ. Но если создать не общий интерфейс, который позволит использовать любой объект вместо T. Я бы хотел только разрешить объекты, которые наследуются от TableRecord. –

+0

Ну нет, не совсем. Это приведет к ошибке во время выполнения, потому что ваша не-общая реализация обычно просто вызывает общую реализацию с литой. Кроме того, вы можете реализовать ITableDataAccessLayer явно в UserDataAccessLayer.Это та же концепция, за исключением того, что вы просто сужаете типы, которые компилятор примет. – Josh

0

составляет ли это?

UserDataAccessLayer udal = new UserDataAccessLayer(); 
ITableDataAccessLayer<TTableRecord> itdal = (ITableDataAccessLayer<TTableRecord>)udal; 

или даже просто

ITableDataAccessLayer<TTableRecord> itdal = new UserDataAccessLayer(); 

как это общий интерфейс, он, вероятно, должен знать, какой тип это?

было бы полезно знать сообщение об ошибке. который обычно проливает свет на эту тему.

4

Действительно, вы хотите ковариации. Пара очков.

Во-первых, поймите, почему иногда это должно быть незаконным. Возьмите, например, IList. Предположим, у вас есть IList<Giraffe>, список жирафов. Можете ли вы преобразовать это в список животных? Нет, не безопасно. Да, список жирафов - это список животных в том смысле, что все в списке - животное. Но списки изменяемы; вы можете вставить тигра в список животных, но если это действительно список жирафов, то это должно потерпеть неудачу. Поскольку это небезопасно, мы не будем делать ковариацию IList на C# 4.

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

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

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

+0

Я знаю, что в данный момент это не имеет значения, но я думаю, что это должно было быть разрешено и просто вызвало исключения во время выполнения. Если мой единственный и единственный совместимый с .NET существует, он пытается слишком сильно защитить разработчика от самих себя. Еще один большой пример - заставить оператор break в операторах switch, почему я не могу пропустить ситуацию, если это то, что я хочу? Или имел переключатель (переменный, истинный), чтобы разрешить падение корпуса и т. Д. –

+4

OK. Опишите, как вы хотите, чтобы среда выполнения определяла во время выполнения, когда выбрасывать исключение. Теперь опишите общую стоимость вашего предлагаемого изменения: поставить проверку типа на КАЖДУЮ запись, любой метод вызова и т. Д. Обратите внимание, что 100% стоимости исполнения несет код CORRECT. И теперь попробуйте продать это скептически настроенное население разработчиков, которые уже думают, что «управляемый код равен медленному коду». Мы не поймаем этот материал во время компиляции, чтобы быть патерналистским по отношению к вам, мы делаем это, потому что в противном случае мы должны сделать ваш код ОЧЕНЬ МЕДЛЕННЫМ. –

+1

Что касается вашего вопроса о провале: провалиться было плохой идеей, потому что это почти никогда не то, что на самом деле хочет пользователь, и это невидимая ошибка. Если вы хотите провалиться, вы можете легко сделать это явным: просто скажите «goto case default»; если вы хотите перейти к случаю по умолчанию. –

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