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)



Если это невозможно сделать, может ли кто - нибудь привести мне пример или причину, почему бы и нет? Или это просто будет слишком сложно реализовать в компиляторе?
554   5  

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

    Ничего не найдено.