Попытка вернуть конкретный список объектов с типом интерфейса [duplicate]

Я открыл старый проект, и когда мне захотелось его построить, я получал много ошибок, которые были очень неприятными. Я заменил компиляцию на реализацию и добавил все необходимые коды. Наконец, проект успешно строился.

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    androidTestImplementation ('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    implementation 'com.android.support:support-v4:27.1.1'
    implementation 'com.android.support:cardview-v7:27.1.1'
    implementation 'com.android.support:customtabs:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.facebook.android:facebook-android-sdk:4.+'
    implementation 'com.facebook.android:account-kit-sdk:4.+'
    implementation 'com.android.volley:volley:1.1.0-rc2'
    implementation 'com.google.android.gms:play-services-ads:15.0.1'
    testImplementation 'junit:junit:4.12'
}
22
задан santosh singh 15 September 2010 в 19:51
поделиться

5 ответов

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

List<Cat> cats = new List<Cat>();
List<Animal> animals = cats; // Trouble brewing...
animals.Add(new Dog()); // hey, we just added a dog to a list of cats...
cats[0].Speak(); // Woof!

Хорошо, собака, мои кошки, это плохо.

Функция, которую вы хотите, называется «общей ковариацией», и она поддерживается в C # 4 для интерфейсов, которые, как известно, являются безопасными. IEnumerable<T> не имеет никакого способа записи в последовательность, поэтому он безопасен.

class Animal    
{    
    public virtual void Play(IEnumerable<Animal> animals) { }    
}    
class Cat : Animal    
{    
    public override void Play(IEnumerable<Animal> animals) { }    
}    
class Program    
{    
    static void Main()    
    {    
        Cat cat = new Cat();    
        cat.Play(new List<Cat>());    
    }    
}  

Это будет работать на C # 4, потому что List<Cat> конвертируется в IEnumerable<Cat>, который можно конвертировать в IEnumerable<Animal>. Нельзя использовать Play IEnumerable<Animal>, чтобы добавить собаку к тому, что на самом деле является списком кошек.

45
ответ дан Eric Lippert 5 September 2018 в 10:17
поделиться

Вы могли бы сделать несколько вещей. Например, элементы списка относятся к Animal

. Использование вашего кода:

cat.Play(new List<Cat>().Cast<Animal>().ToList());

Другим является создание Animal общего, поэтому cat.Play(new List<Cat>()); будет работать.

class Animal<T>
{
    public virtual void Play(List<T> animals) { }
}
class Cat : Animal<Cat>
{
    public override void Play(List<Cat> cats)
    {
    }
}

class Program
{
    static void Main(string[] args)
    {
        Cat cat = new Cat();
        cat.Play(new List<Cat>());
    }
}

Другим способом является не создание Animal общего, а метода Play и ограничение этого на T : Animal

class Animal
{
    public virtual void Play<T>(List<T> animals) where T : Animal { }
}
class Cat : Animal
{
    public override void Play<T>(List<T> animals) 
    {
    }
}

Наконец, если вы находитесь на C # 4 и вам нужно только перечислить список и не изменять его, проверьте ответ Эрика Липперта на IEnumerable<Animal>.

13
ответ дан Anthony Pegram 5 September 2018 в 10:17
поделиться

использовать метод расширения Cast ()

so:

class Program
{
    static void Main(string[] args)
    {
        Cat cat = new Cat();
        cat.Play(new List<Cat>().Cast<Animal>());
    }
}

Причиной этого является b / c .net 3.5 не поддерживает ковариацию, но 4.0 делает :)

3
ответ дан Jose 5 September 2018 в 10:17
поделиться

Вы ищете общую ковариацию коллекции. Очевидно, что эта функция не поддерживается версией C #, которую вы используете.

Вы можете обойти это с помощью метода расширения Cast<T>(). Имейте в виду, однако, что это создаст копию исходного списка вместо передачи оригинала как другой тип:

cat.Play((new List<Cat>()).Cast<Animal>().ToList());
10
ответ дан Justin Niessner 5 September 2018 в 10:17
поделиться

Все упоминают метод литья уже. Если вы не можете обновить до 4.0, способ скрыть приведение

class Cat : Animal
{
    public override void Play(List<Animal> animal)
    {
         Play((List<Cat>)animal);
    }
    public virtual void Play(List<Cat> animal)
    {
    }
}

Это трюк IEnumable и IEnumarable<T> для GetEnumerator

2
ответ дан Scott Chamberlain 5 September 2018 в 10:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: