2013-03-11 2 views
6

Итак, у меня есть класс, который принимает параметр типового типа и выполняет небольшую специальную обработку, если параметр типа является подклассом заданного типа.Кастинг родовой тип в запросе linq

IEnumerable<T> models = ... 

// Special handling of MySpecialModel 
if (filterString != null && typeof(MySpecialModel).IsAssignableFrom(typeof(T))) 
{ 
    var filters = filterString.Split(...); 
    models = 
     from m in models.Cast<MySpecialModel>() 
     where (from t in m.Tags 
       from f in filters 
       where t.IndexOf(f, StringComparison.CurrentCultureIgnoreCase) >= 0 
       select t) 
       .Any() 
     select (T)m; 
} 

Но я получаю исключение на последней строке

Cannot convert type 'MySpecialModel' to 'T' 

Если изменить код, чтобы использовать as вместо литья, я получаю эту ошибку.

The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint. 

Что мне здесь не хватает?

Update

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

+0

Вы установили ограничение 'where T: class' в ваш общий класс? –

+0

@ danradu Нет, но это не сработает в моем случае, поскольку общий класс может принимать как ссылочные, так и значения типа. –

+0

@ p.s.w.g, см. Обновление – smartcaveman

ответ

3

ли Select(x => (MySpecialModel)x)

Метод LINQ Cast<T> будет работать только для элементов литейных к тому, что элемент уже есть (например, базового типа, производного типа, или интерфейс). Он не предназначен для создания объектов, которые могут быть выбраны для целевого типа. (Например, new List<int>{1,2,3}.Cast<long>() сгенерирует исключение, а также.

выше ответ не был неправ, но это не решает вопрос.

Просто потому, что вы оказались с отражением, что общий параметр связан с заданный тип, не означает, что компилятор знает, что это так. Для выполнения этой работы вам нужно будет наложить ваш экземпляр T на общий тип (например, object), а затем передать его конкретному типу, например (изменение последней строки в вашем запросе до select (T)(object)m должно сделать трюк.

+0

Пробовал 'Выбрать (x => (T) x)'. тот же результат –

+0

ohh my bad, измените свою последнюю строку 'select (T) (объект) m') - Я не прочитал вопрос полностью, прежде чем отвечать – smartcaveman

+0

Спасибо. Двухлистный работает, но выглядит странно. В конце концов я закончил работу с '.Cast ()' (что эквивалентно и более эстетично). Если вы знаете какую-либо причину, то двойной бросок будет лучше, пожалуйста, дайте мне знать. –

1

Чтобы использовать ключевое слово as, поставьте class ограничение на ваш общий параметр:

void MyMethod<T>(T item) where T : class 
{ 
    //... 
} 
+0

Я не хочу этого делать, поскольку этот класс также может быть используется для 'struct'. –

1

Если вы знаете, что общий тип всегда будет класс, вы можете добавить ограничение типа на классе:

public class Test<T> where T : class {} 

В противном случае выполнить двойной бросок через объект, как smartcaveman предложил:

.Select(x => (T)(object)x); 
+0

прочитал его снова - я тоже снялся в первый раз, но он действительно спрашивал, как передать параметр' T' непосредственно в 'MySpecialModel', что невозможно. – smartcaveman

0

Вы могли бы применить Nullable<T> ограничение - это должно позволить возможность отбрасывать (по крайней мере, использовать «как»).

1

Наиболее простым исправлением является приведение к object первым перед броском в T:

select (T)(object)m; 

Проблема ваша проверка происходит во время выполнения, но компилятор не знает, что T должен быть экземпляром MySpecialModel в заявлении if.Поэтому он просто видит, что вы пытаетесь применить к произвольному типу T от MySpecialModel, что небезопасно, следовательно, ошибка.

2

Попробуйте следующее

select (T)(object)m; 

Во время выполнения вы убедились, что T является подтипом MySpecialModel но компилятор не имеет доступа к этой информации во время компиляции. Он просто видит попытку конверсии между двумя несвязанными типами: T и MySpecialModel.

Чтобы обойти это, вам необходимо использовать object в качестве среднего человека. Компилятор понимает, как преобразовать MySpecialModel в object и перейти от object в T.

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