У меня есть тип Shelter, который должен быть ковариантным, чтобы переопределение в другом классе * могло возвращать Shelter<Cat>
, где ожидается Shelter<Animal>
. Так как классы не могут быть совместными или контравариантными в C #, я добавил интерфейс:
public interface IShelter<out AnimalType>
{
AnimalType Contents { get; }
}
Однако, есть место, где IShelter (тип времени компиляции) назначается новое животное, где мы точно знаем, что устанавливаемое животное будет Кошкой. Сначала я подумал, что могу просто добавить набор к свойству Contents и сделать:
IShelter<Cat> shelter = new Shelter(new Cat());
shelter.Contents = new Cat();
Но добавить сеттер невозможно;
Error CS1961 Invalid variance: The type parameter 'AnimalType' must be invariantly valid on 'IShelter<AnimalType>.Contents'. 'AnimalType' is covariant.
Это имеет смысл, потому что в противном случае я мог бы передать укрытие этой функции:
private static void UseShelter(IShelter<Animal> s)
{
s.Contents = new Lion();
}
Однако я не собираюсь этого делать. Было бы неплохо иметь какой-то способ пометить сеттер как инвариант, чтобы функция UseShelter могла назначать только Animal, и чтобы это выполнялось во время компиляции. Причина, по которой мне это нужно, заключается в том, что в моем коде есть место, которое знает, что оно имеет Shelter<Cat>
, и ему необходимо переназначить свойство Contents для нового Cat.
Обходной путь, который я нашел до сих пор, состоит в том, чтобы добавить проверочную проверку типов во время выполнения в явной функции set; Juck!
public void SetContents(object newContents)
{
if (newContents.GetType() != typeof(AnimalType))
{
throw new InvalidOperationException("SetContents must be given the correct AnimalType");
}
Contents = (AnimalType)newContents;
}
Параметр должен иметь тип объекта, чтобы эту функцию можно было указать в интерфейсе. Есть ли способ применить это во время компиляции?
* Для пояснения есть функция: public virtual IShelter<Animal> GetAnimalShelter()
, которая переопределяется и возвращает IShelter<Cat>
:
public override IShelter<Animal> GetAnimalShelter(){
IShelter<Cat> = new Shelter<Cat>(new Cat());
}
Ниже приведен минимальный рабочий пример, включающий большую часть приведенного выше кода:
class Animal { }
class Cat : Animal { }
class Lion : Animal { }
public interface IShelter<out AnimalType>
{
AnimalType Contents { get; }
void SetContents(object newContents);
}
class Shelter<AnimalType> : IShelter<AnimalType>
{
public Shelter(AnimalType animal)
{
}
public void SetContents(object newContents)
{
if (newContents.GetType() != typeof(AnimalType))
{
throw new InvalidOperationException("SetContents must be given the correct AnimalType");
}
Contents = (AnimalType)newContents;
}
public AnimalType Contents { get; set; }
}
class Usage
{
public static void Main()
{
IShelter<Cat> catshelter = new Shelter<Cat>(new Cat());
catshelter.SetContents(new Cat());
catshelter.SetContents(new Lion()); // should be disallowed by the compiler
}
}