Использование yield return для возврата объектов, которые наследуют форму базового типа - PullRequest
2 голосов
/ 29 октября 2009

У меня есть базовый класс с именем Media с двумя наследующими классами Photo и Video. Я пытаюсь создать коллекцию для базового класса мультимедиа для хранения этих фото и видео объектов. Итак, я создал класс MediaList следующим образом:

public class MediaList: ICollection<Media>
{
    private readonly XElement _mediaElement;

    public MediaList(XElement mediaElement)
    {
        _mediaElement = mediaElement;            
    }

    public IEnumerator<Media> GetEnumerator()
    {
        foreach (XElement element in _mediaElement.Elements())
        {
            Media media;
            switch (element.Name.LocalName)
            {
                case "video":
                    media = new Video(element);
                    break;
                case "photo":
                    media = new Photo(element);
                    break;
                default:
                    media = null;
                    break;
            }
            yield return media;
        }
    }

    //Rest of ICollection Implementation
}

Когда я повторяю список, я получаю следующее исключение:

Значение «Tool.Photo» не относится к типу «Tool.Video» и не может использоваться в этой общей коллекции.

Если я возвращаю Media объект, почему он вызывает исключение? Есть ли лучший способ обойти это?

Ответы [ 4 ]

3 голосов
/ 29 октября 2009

Попробуйте это ...

class Program
{
    static void Main(string[] args)
    {
        var array = new string[] { "video", "photo", "hurf", "photo" };
        var ml = new MediaList(array);
        foreach(var element in ml)
            Console.WriteLine(element.GetType().Name);
        Console.Read();
    }
}

public class Media { }
public class Video : Media { }
public class Photo : Media { }

public class MediaList
{
    private string[] elements;
    public MediaList(string[] elements) { this.elements = elements; }
    public IEnumerator<Media> GetEnumerator()
    {
        foreach (string s in elements)
            switch (s)
            {
                case "video":
                    yield return new Video();
                    break;
                case "photo":
                    yield return new Photo();
                    break;
            }
    }
}

Вы можете добавить это в консольное приложение, чтобы протестировать его.

Обратите внимание на пару разных вещей. Сначала , вы никогда не дадите return null. Это не имеет никакого отношения к вашей проблеме, но никто не ожидает, что перечислимое число возвратит ноль и вызовет у вас проблемы позже. Второй , я не кастую то, что возвращаю. Все приведенные значения неявно или обрабатываются компилятором, поэтому мне не нужно этого делать. Третий , это компилирует и работает, как и ваш исходный код. Ваша проблема возникает где-то еще, как вы обнаружите, если перетащите этот код в консольное приложение и протестируете его.

2 голосов
/ 29 октября 2009

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

1 голос
/ 29 октября 2009

Не следует ли набирать метод с возвращаемым значением для возврата IEnumerable<Media>, а не IEnumerator<Media>?

Но, в сущности, этот паттерн оставляет у меня неприятный запах. Каждый раз, когда вы используете foreach в этой «коллекции», вы будете создавать (создавать) новые экземпляры каждого Photo или Video объекта в списке _mediaElement.Elements(). Вы действительно этого хотите?

0 голосов
/ 29 октября 2009

Похоже, вы пытаетесь добавить Media экземпляров в коллекцию Video, и это взрывается, потому что один или несколько из них Photo - вы пытаетесь добавить фотографию к видео коллекция. Ошибка в коде вашего клиента, а не в сгенерированном перечислителе.

...