Я хотел сделать следующее наследование с включенными дженериками, но окончательное приведение
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.
о, отлично, но что, если бы у меня был метод, который принимает T в качестве параметра в A<T>?
Ваш код больше не компилируется. Вам нужно реализовать Method
в Implementor
.
Когда вы реализуете Method
, вы увидите, что он принимает параметр ImplementorX
. XBase
не ImplementorX
, поэтому вы не можете сниматься. Попробуйте изменить метод интерфейса на Method(XBase param)
, а затем вы можете использовать out
, и это сработает.
Основная цель этого T in Method заключалась в том, чтобы гарантировать, что внутри Implementor могут быть переданы объекты типа ImplementorX. Я хотел, чтобы это было так, чтобы не требовалось выполнять приведение в методе соответствующих реализаторов. Хотя кажется, что это невозможно.
Допустим, у меня есть два типа, Apple
и Banana
; оба из которых реализуют IFruit
. Сейчас я создаю коллекцию BowlOf<Apple>
. Если бы я мог применить это к BowlOf<IFruit>
(что вы делаете с a as A<XBase>
), то я смог бы добавить Banana
к моему BowlOf<Apple>
— а это неправильно, и поэтому приведение не разрешено.
Если вы делаете явное приведение:
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 спасибо за ответ. Я просто хотел сопоставить авторский код с реальными объектами-примерами, чтобы показать, почему это мешает кастингу. я обновил свой ответ
Это гораздо лучший пример! Немного графики, но я думаю, что это помогает.
@Enigmativity да, я добавил простую схему :)
Вау, отличное объяснение! Большое спасибо!
@P.Steininger Я рад, что это помогло вам :)
Вы должны узнать о ковариантности/контравариантности. Вы можете заставить это работать, изменив свой интерфейс:
public interface A<out T> where T : XBase