Почему c # не поддерживает объект с интерфейсом в качестве параметра? - PullRequest
5 голосов
/ 08 января 2010

У меня есть следующее объявление класса:

public class EntityTag : BaseEntity, ITaggable

У меня есть вспомогательный метод Html:

public static string TagCloud(this HtmlHelper html, IQueryable<ITaggable> taggables, 
  int numberOfStyleVariations, string divId)

Это мой ASP.NET MVC ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IQueryable<EDN.MVC.Models.EntityTag>>" %>
<%@Import Namespace="EDN.MVC.Helpers" %>
<%= Html.TagCloud(Model, 6, "entity-tags") %>

Когда я передаю коллекцию IQueryable в ascx, я получаю эту ошибку:

Сообщение об ошибке компилятора: CS1928: «System.Web.Mvc.HtmlHelper>» не содержит определения для «TagCloud» и лучшая перегрузка метода расширения «EDN.MVC.Helpers.EdnHelpers.TagCloud (System.Web.Mvc .HtmlHelper, System.Linq.IQueryable, int, string) 'имеет недопустимые аргументы

Если я попытаюсь явно преобразовать коллекцию объектов следующим образом:

    public static string TagCloud(this HtmlHelper html, IQueryable<Object> taggables, int numberOfStyleVariations, string divId)
    {
        var tags = new List<ITaggable>();
        foreach (var obj in taggables)
        {
            tags.Add(obj as ITaggable);
        }
        return TagCloud(html, tags.AsQueryable(), numberOfStyleVariations, divId);
    }

Я получаю ту же ошибку - передаваемые значения не нравятся компилятору.

Разве мой класс EntityTag не должен автоматически поддерживаться как IQueryable? Что мне не хватает? Это должно быть что-то очевидное. (Надеюсь.)

Ответы [ 3 ]

5 голосов
/ 08 января 2010

По сути, вы пытаетесь передать объект неуниверсального типа IQueryable методу, который принимает универсальный IQueryable<ITaggable>, с которым компилятор не может "сопоставить", что приводит к CS1928 (так как два типа на самом деле разные).

В вашей перегрузке, которая принимает IQueryable<object> (которая уже выполняет необходимое преобразование в общий список), вам просто нужно вызвать общую версию AsQueryable вместо неуниверсальной, как таковую:

public static string TagCloud(this HtmlHelper html, IQueryable taggables, int numberOfStyleVariations, string divId)  
{  
    var tags = new List<ITaggable>();  
    foreach (var obj in taggables)  
    {  
        tags.Add(obj as ITaggable);  
    }  
    return TagCloud(html, tags.AsQueryable<ITaggable>(), numberOfStyleVariations, divId);  
}  

Позвольте мне также добавить, что IQueryable<T> происходит от IQueryable, что означает, что не все IQueryable объекты IQueryable<T>, что делает необходимым преобразование. Если бы ситуация была обратная, т. Е. Ваш «настоящий» вспомогательный метод был определен для обработки IQueryable объектов, то у вас, конечно, не возникло бы проблем с передачей IQueryable<T> этому методу (поскольку все IQueryable<T> объекты фактически IQueryable).

Per Craig Stuntz, гораздо более элегантное решение с использованием функций LINQ: <%= Html.TagCloud(Model.Select(t => (ITaggable)t), 6, "entity-tags") %>. Вы также можете использовать <%= Html.TagCloud(Model.Cast<ITaggable>(), 6, "entity-tags") %>, если ваш провайдер поддерживает этот запрос.

2 голосов
/ 08 января 2010

C # 4.0 будет поддерживать это. Поиск "Ковариация и Контравариантность в C # 4"

1 голос
/ 08 января 2010

Ваш класс EntityTag равен IQueryable, однако компилятор не знает, что ваш список тегов на самом деле представляет собой список EntityTag объектов, он знает только, что это список объектов, реализующих ITaggable, что вероятно, не IQueryable.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...