C# абстрактный класс возвращает перечислитель производных типов
Если у меня есть абстрактный класс, могу ли я каким-либо образом вернуть перечислитель типа производного класса? Или мне придется использовать универсальные методы в базовом классе или универсальный метод? Вот действительно тупой пример того, что я пытаюсь сделать -
public abstract class Person {
public IEnumerable<MyType> Search() {
DbDataReader reader = Database.Instance.ExecuteReader(sql);
while(reader.Read()) {
MyType row = new MyType();
row.Load(reader);
yeild return row;
}
}
private Load(DbDataReader reader) {
//load instance from reader row
}
//declare properties that can be searched, such as Location
}
public class Programmer : Person {
//declare properties that can be searched, such as Language
}
Тогда где-нибудь еще я хотел бы иметь возможность позвонить
Programmer programmer = new Programmer();
programmer.Location = "My city";
programmer.Language = "C#";
foreach(Programmer programmer in programmer.Search())
{
//display list of c# programmers in my city
}
Я знаю, что могу сделать это с помощью универсального метода, например Search<T>(), но я хотел бы иметь возможность вызывать функцию поиска из класса, который точно не знает тип человека. (например, базовый класс для обработчика AJAX)
Если это невозможно сделать, может ли кто - нибудь привести мне пример или причину, почему бы и нет? Или это просто будет слишком сложно реализовать в компиляторе?
5 ответов:
Если вы ищете подход с этим "красивым" использованием, я предлагаю метод расширения. С помощью Ext вам не нужно дважды определять эффективный тип сущности.
programmer.Search<Programmer>()=>programmer.Search()public static PersonExtensions { public static IEnumerable<TType> Search<TType>(this TType row) : where TType : new(), Person { DbDataReader reader = Database.Instance.ExecuteReader(sql); while(reader.Read()) { var row = new TType() row.Load(reader); yeild return row; } } }
Нет никаких причин, по которым вы не можете сделать свой метод поиска универсальным:
public IEnumerable<T> Search<T>() where T : MyType, new() { DbDataReader reader = Database.Instance.ExecuteReader(sql); while(reader.Read()) { T row = new T(); row.Load(reader); yield return row; } }И вызовите
programmer.Search<Programmer>().Откуда взялось
MyTypeКстати? Должно ли это бытьPerson?
См. http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx
Ковариация на виртуальных типах возвращаемых методов-довольно часто запрашиваемая функция, и я бы использовал ее, если бы она у меня была. Это никогда не делалось бар, потому что (1) CLR не поддерживает его; мы должны были бы либо сгенерировать много вспомогательного кода за кулисами, чтобы заставить его работать, или убедить команду CLR изменить (команда C++/CLI сделала первое) (2) в большинстве случаев вы можете сгенерировать необходимые вспомогательные функции самостоятельно довольно легко; просто сделайте" новый " метод, который имеет правильный тип возврата, который делегирует его реализацию виртуальному методу, и (3) Андерс не считает его особенно важной функцией. - ЭрикТак что, нет, это невозможно, И да, это потому, что это слишком трудно.
@ben dotnet: тот факт, что не нужно явно передавать эффективный тип, не связан с тем, что это метод расширения. Это из-за типа механизм логического вывода и которые могли бы произойти, а также на какой-либо способ (расширение или регулярный).
В таком методе прототип
public static IEnumerable<TType> Search<TType>(this TType row) : where TType : new(), Person
TTypeИзвестно, что компилятор будет таким же 4 раза, когда он встречается в строке. Таким образом, при вызове метода достаточно знать типprogrammer, чтобы знать, что он будет одного типа между the<...>. Вот почему мы можем его опустить. Он также работает, если нет параметра с префиксом ключевого словаthis.
Можно использовать универсальный тип в базовом классе и определить универсальный тип в производном классе как производный класс.
public abstract class Person<T> where T : Person<T> { public IEnumerable<T> Search() { DbDataReader reader = Database.Instance.ExecuteReader(sql); while (reader.Read()) { var row = new T(); row.Load(reader); yield return row; } } protected virtual void Load(DbDataReader reader){} } public class Programmer : Person<Programmer>{}
Comments