C # абстрактный класс, возвращающий перечислитель производного типа - PullRequest
4 голосов
/ 23 мая 2011

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

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>(), но я хотел бы иметь возможность вызывать функцию поиска из класса, который точно не знает тип, которым является Person (например, базовый класс для обработчика AJAX)

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

Ответы [ 5 ]

3 голосов
/ 23 мая 2011

См. http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

Ковариантность типов возвращаемых данных виртуальных методов - довольно часто запрашиваемая функция, и я бы использовал ее, если бы имел. Это никогда не делало планку, потому что (1) CLR не поддерживает это; нам нужно было бы либо сгенерировать много вспомогательного кода за кулисами, чтобы заставить его работать, либо убедить команду CLR измениться (команда C ++ / CLI сделала первое) (2) в большинстве случаев вы можете сгенерировать необходимые вспомогательные функции самостоятельно довольно легко; просто создайте «новый» метод с правильным возвращаемым типом, который делегирует его реализацию виртуальному методу, и (3) Андерс не считает его особенно важной функцией. - Эрик

Итак, нет, это невозможно, и ДА, потому что это слишком сложно.

3 голосов
/ 23 мая 2011

Нет причин, по которым вы не можете сделать свой метод поиска универсальным:

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?

1 голос
/ 23 мая 2011

@ ben dotnet: факт отсутствия явной передачи эффективного типа не связан с тем, что это метод расширения. Это происходит из-за механизма вывода типа и также может происходить с любым методом (расширенным или обычным).

В таком способе прототип

public static IEnumerable<TType> Search<TType>(this TType row) : where TType : new(), Person

TType известен компилятору как 4 раза, когда он встречается в строке. Таким образом, при вызове метода достаточно знать тип programmer, чтобы знать, что он будет того же типа, что и <...>. Вот почему мы можем опустить это. Это также работает, если нет префикса с ключевым словом this.

1 голос
/ 23 мая 2011

Если вы ищете подход с таким «красивым» использованием, я предлагаю ExtensionMethod.С 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;
        }
    }
}
0 голосов
/ 23 мая 2011

Вы можете использовать универсальный тип в базовом классе и определить свой универсальный тип в производном классе как производный класс.

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>{}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...