Ограничение типа возврата статического метода к текущему типу класса

#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 ключевого слова на уровне типа, который был бы доступен в статических методах. Конечно, я понимаю, что это было бы слишком сложно, учитывая, что тот факт, что статические методы «наследуются», сам по себе удивителен. 🙂