Contravariance / Covariance - невозможно преобразовать класс в интерфейс - PullRequest
0 голосов
/ 11 декабря 2019

У меня есть следующий код

public interface IInterface
{
}
public class GenericClass<TSomeClass>
    where TSomeClass : class
{
    public TSomeClass SomeMethod(TSomeClass someClass = null)
    {
        return SomeClass.SomeClassStaticInstance;                   //ERROR:Cannot implicitly convert type 'SomeClass' to 'TSomeClass'
        return (TSomeClass)SomeClass.SomeClassStaticInstance;       //ERROR:Cannot convert type 'SomeClass' to 'TSomeClass'
        return SomeClass.SomeClassStaticInstance as TSomeClass;     //Works when "where TSomeClass : class" clause added
    }
}
public class SomeClass : IInterface
{
    public static SomeClass SomeClassStaticInstance = new SomeClass();
}

Он генерирует ошибки времени компиляции, отмеченные в комментариях к соответствующим строкам.

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

Я пытался изменить GenericClass<TSomeClass> на GenericClass<out TSomeClass>, но затем я получаю еще одну ошибку времени компиляции Only interface and delegate type parameters can be specified as variant., котораясохраняется, даже если я удаляю предложение where TSomeClass : class.

Чего мне не хватает ... это, очевидно, работает, потому что я могу "заставить его" с помощью предложения where TSomeClass : class и оператора return SomeClass.SomeClassStaticInstance as TSomeClass;!

Мне действительно нужен where TSomeClass : class, в противном случае я получаю еще одну ошибку времени компиляции с TSomeClass someClass = null ... A value of type '<null>' cannot be used as a default parameter because there are no standard conversions to type 'TSomeClass'.

Так что это в основном ошибки времени компиляции до самого конца! Спасибо.

Ответы [ 3 ]

1 голос
/ 11 декабря 2019

I would like to know why I can't just use the first line that generates an error? SomeClass implements IInterface but I have to mess around with the as keyword.

Вы должны использовать (TSomeClass)YourInstance cast, поскольку язык предназначен для предотвращения любых предположений о приведении обобщенных аргументов. Подробнее .

Поскольку неявное преобразование по типу аргумента TSomeClass не может быть предсказано, если вы не наложите ограничение на менее унаследованный тип, например интерфейс. Вы должны изменить ограничение на where TSomeClass : IInterface для дисперсии.

Использование ключа as сработало, потому что оно вернет ноль, если тип несовместим, поэтому вам нужно ограничение class. После изменения ограничения на интерфейс оператор as станет недействительным.

В вашем фабричном методе вы должны вернуть IInterface, чтобы аргумент универсального типа мог принимать прямое приведение.

1 голос
/ 11 декабря 2019

Я полагаю, что это будет работать:

public interface IInterface
{
}

public class GenericClass<TSomeClass>
    where TSomeClass : IInterface
{
    //Not sure what the input parameter is for?
    public IInterface SomeMethod(TSomeClass someClass = null)
    {
        return SomeClass.SomeClassStaticInstance;                  
    }
}

public class SomeClass : IInterface
{
    public static IInterface SomeClassStaticInstance = new SomeClass();
}

Однако, как отмечали другие, вопрос здесь действительно заключается в том, для чего используются дженерики в этом случае - возможно, есть лучший выбор, как структурироватьВаш код?

Очень трудно сказать, для чего это будет сделано из структуры, которую вы нам дали.

1 голос
/ 11 декабря 2019

На данный момент единственным ограничением является то, что TSomeClass является классом, поэтому если вам нужно создать экземпляр GenericClass с типом, который не является SomeClass, или с типом, который происходит от него, return SomeClass.SomeClassStaticInstance; явноне будет работать.

Например:

var genericClass = new GenericClass<SomeRandomClass>(); // fine
var randomClass = genericClass.SomeMethod(new SomeRandomClass()); // not fine, a SomeClass is returned!

Я не уверен, куда в данный момент вписывается IInterface?

Может быть, вы хотите SomeMethodвернуть объект, который реализует IInterface;если это так, вам нужно дополнительно ограничить свой универсальный тип, и ваш метод должен вернуть IInterface:

public class GenericClass<TSomeClass> where TSomeClass : class, IInterface
{
    public IInterface SomeMethod(TSomeClass someClass = null)
    {
        return SomeClass.SomeClassStaticInstance; //This now compiles!
    }
}

Что касается того, почему return SomeClass.SomeClassStaticInstance as TSomeClass; работал изначально, это было просто совпадение.

as - безопасный оператор приведения, который возвращает null, если попытка приведения не удалась. Поскольку вы TSomeClass ограничены классом, возвращаемое значение null будет допустимым. Однако вы всегда будете возвращать null, если указанный вами общий тип не будет SomeClass.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...