#c#
#c#
Вопрос:
У меня есть иерархия классов и статический метод в базовом классе, поэтому подклассы тоже имеют этот статический метод. Я хотел бы определить статический метод, например
public static IEnumerable<T> GetList<T>()
where T : BaseClass, new()
{
return new List<T>() { ... };
}
поэтому я мог бы вызвать
var list = ChildClass.GetList();
и список был бы IEnumerable<ChildClass>
. Но: я не знаю, как сообщить компилятору, чтобы он определял общий тип как тип класса, из которого я вызываю, поэтому это использование приводит к ошибке, и я должен исправить это как-то избыточно
var list = ChildClass.GetList<ChildClass>();
Я мог бы использовать методы расширения (у них есть this
аргумент, который позволяет компилятору перехватывать), но я хочу сделать это без создания экземпляра ChildClass
.
Есть ли способ выполнить это без повторения?
Ответ №1:
Вы не можете заставить компилятор выводить класс, из которого вы вызываете, потому что технически вы всегда вызываете из класса, в котором определен метод.
Компилятор, позволяющий вместо этого использовать производное имя класса, просто удобен.
Ответ №2:
Если вы можете смириться с уродливым взломом, вы можете сделать BaseClass
его универсальным и передать тип дочернего класса в качестве аргумента для универсального параметра, например:
class BaseClass<T> where T : BaseClass<T>, new()
{
public static IEnumerable<T> GetList()
{
return new List<T>() { new T() }; // whatever
}
}
class Child : BaseClass<Child>
{
}
class Child2 : BaseClass<Child2>
{
}
Теперь Child.GetList()
возвращает an IEnumerable<Child>
и Child2.GetList()
возвращает an IEnumerable<Child2>
.
Комментарии:
1. Пока кто-то не создаст
EvilChild: BaseClass<Child1>
2. @Servy Да, вот почему уродливые взломы называются уродливыми и взломами.
Ответ №3:
Если вы хотите знать, почему компилятор не может разрешить тип, читайте дальше:
Во-первых, статические методы привязаны к типам, а не к объектам. Таким образом, здесь не может быть никакого полиморфного поведения. Таким образом, тот факт, что у вас есть дочерняя иерархия базовых классов, здесь не очень важен, поскольку методы привязаны к двум совершенно разным объектам.
Реальная проблема заключается в возможностях вывода типов компилятора C #. Вы должны предоставить компилятору некоторую информацию о том, что это за тип T
. В противном случае он не сможет его вывести.
Вы также не можете предоставить эту информацию с левой стороны, поскольку вывод типа C # выполняется справа налево. Другими словами:
var ints = new {};
Все в порядке. Поскольку тип справа является анонимным методом, а целые числа преобразуются в единицу. Но:
int[] ints = new [] {};
Не будет работать. Компилятор жалуется, что «не найден наилучший тип для неявно типизированного массива». Другими словами, компилятор не может использовать информацию с левой стороны, чтобы выяснить, какой тип должен быть с правой стороны.
В вашем вопросе
var list = ChildClass.GetList();
Не дает компилятору никакой информации о том, что такое T ! Даже если бы у вас был тип вместо var, это ничего бы не изменило.
Комментарии:
1. Приятно, спасибо за ответ! Я знаю о помехах справа налево, но я не уверен, насколько это применимо там. Если вы рассматриваете цепные вызовы
list.Where(...).Select(...)
, то информация о типе фактически перемещается вправо — вам не нужно вводить лямбды в last select, потому что вы знаете, чтоlist
это такое. Точно так же МОЖЕТ существоватьTHIS
эквивалентthis
ключевого слова на уровне типа, который был бы доступен в статических методах. Конечно, я понимаю, что это было бы слишком сложно, учитывая, что тот факт, что статические методы «наследуются», сам по себе удивителен. 🙂