Общее наследование C#, приведение базового класса

Я хотел сделать следующее наследование с включенными дженериками, но окончательное приведение a as A<XBase> всегда приводит к нулю, потому что приведение недействительно. Может ли кто-нибудь уточнить, почему этот бросок будет недействительным, а также, возможно, решение этой проблемы.

public class XBase {}
public interface A<T> where T : XBase 
{
    //Edited
    void Method(T param);
}


public class Implementor : A<Implementor.ImplementorX > 
{
    public class ImplementorX : XBase {public int a;}

    //Edited
    void Method(ImplementorX param) {}
}

public class HelloWorld
{
    public static void Main(string[] args)
    {
        var a = new Implementor();
        
        var castRes = a as A<XBase>;
        Console.WriteLine(castRes != null);
    }
}

см. живой пример https://rextester.com/BTNVT61833

EDITED: добавлен метод interface A<T> bc, иначе его можно решить с помощью ответа @DavidG.

Вы должны узнать о ковариантности/контравариантности. Вы можете заставить это работать, изменив свой интерфейс: public interface A<out T> where T : XBase

DavidG 16.03.2022 10:55

о, отлично, но что, если бы у меня был метод, который принимает T в качестве параметра в A<T>?

P. Steininger 16.03.2022 10:58

Ваш код больше не компилируется. Вам нужно реализовать Method в Implementor.

NetMage 16.03.2022 20:51

Когда вы реализуете Method, вы увидите, что он принимает параметр ImplementorX. XBase не ImplementorX, поэтому вы не можете сниматься. Попробуйте изменить метод интерфейса на Method(XBase param), а затем вы можете использовать out, и это сработает.

NetMage 16.03.2022 20:59

Основная цель этого T in Method заключалась в том, чтобы гарантировать, что внутри Implementor могут быть переданы объекты типа ImplementorX. Я хотел, чтобы это было так, чтобы не требовалось выполнять приведение в методе соответствующих реализаторов. Хотя кажется, что это невозможно.

P. Steininger 17.03.2022 14:16

Допустим, у меня есть два типа, Apple и Banana; оба из которых реализуют IFruit. Сейчас я создаю коллекцию BowlOf<Apple>. Если бы я мог применить это к BowlOf<IFruit> (что вы делаете с a as A<XBase>), то я смог бы добавить Banana к моему BowlOf<Apple> — а это неправильно, и поэтому приведение не разрешено.

Enigmativity 25.03.2022 08:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
107
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вы делаете явное приведение:

var castRes = A<XBase>(a);

то вы увидите следующую ошибку:

Unable to cast object of type '' to type '`

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

Абстракции:

// XBase 
public class Animal { }

// class ImplementorX : XBase {public int a;}
public class Bird : Animal
{
    public string WingColor { get; set; }
}

// interface A<T> where T : XBase 
public interface IHat<T> where T : Animal
{
    void Hide(T param);

    T Pull();
}

Конкретные реализации:

// class Implementor : A<Implementor.ImplementorX > 
public class Peacock : IHat<Bird>
{
    // void Method(ImplementorX param) {}
    void IHat<Bird>.Hide(Bird param)
    { }

    public Bird Pull()
    { }
}

и как это можно назвать:

public static void Main(string[] args)
{
    Peacock peacockHat = new Peacock();

    IHat<Animal> animalHat = (IHat<Animal>) peacockHat; // runtime error 'Unable to cast
    // object of type 'HelloWorld.Peacock' to type 'HelloWorld.IHat`1

    // because 
    animalHat.Hide(new Dolphin()); // Hide a dolphin in a peacock hat?  
}

Так что мы не можем hide шляпу Peacock от Dolphin. Это не нормально. CLR не позволяет нам совершать неправильные действия.

Короче:

Короче говоря, представьте, что у вас есть два животных, такие как Wolf и Sheep. И эти классы реализуют интерфейс IAnimal:

public interface IAnimal
{    }

public class Wolf: IAnimal
{    }

public class Sheep : IAnimal
{    }

Итак, классы Sheep, Wolf реализуют унаследованный интерфейс IAnimal:

            IAnimal
             /  \
            /    \
         Sheep   Wolf 

И тогда этих животных можно посадить в клетку:

public class Cage<T> where T : IAnimal
{
    public void Put(T animal)
    {   }
}

Затем вы создаете клетку для Sheep. После этого кто-то хочет бросить Sheep клетку на IAnimal:

Cage<Sheep> sheepCage = new Cage<Sheep>();
sheepCage.Put(new Sheep());

Cage<IAnimal> animalCage = (Cage<Wolf>)sheepCage; // compile error
// if there were no error, then you will be able to do:
animalCage.Put(new Wolf()); // it is not good idea

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

Enigmativity 25.03.2022 08:06

@Enigmativity спасибо за ответ. Я просто хотел сопоставить авторский код с реальными объектами-примерами, чтобы показать, почему это мешает кастингу. я обновил свой ответ

StepUp 25.03.2022 08:34

Это гораздо лучший пример! Немного графики, но я думаю, что это помогает.

Enigmativity 25.03.2022 09:40

@Enigmativity да, я добавил простую схему :)

StepUp 25.03.2022 09:45

Вау, отличное объяснение! Большое спасибо!

P. Steininger 25.03.2022 14:04

@P.Steininger Я рад, что это помогло вам :)

StepUp 25.03.2022 14:34

Другие вопросы по теме