Извлечь строки доходности в общий метод - PullRequest
0 голосов
/ 05 апреля 2011

Мы используем автоматически сгенерированный код (SubSonic3 с шаблоном Repository) с нашим кодом, и таких строк много.

public IEnumerable<MyModels.StatusLookup> GetAll()
    {
        var results = Database.Current.pStatusLookupLoadAll()
            .ExecuteTypedList<MyModels.StatusLookup>();
        if (results.IsNull()) yield break;
        foreach (var m in results)
        {
            ..Common logic lines...
            ..Common logic lines...
            yield return m;
        }
    }

То, что я хотел бы сделать, - это преобразовать линии доходности в общий метод. Но я не знаю, смогу ли я из-за того, как работает yield.

Затем, когда у нас есть собственный код, который вызывает базу данных вне автоматически сгенерированного кода хранилища, я могу затем вызвать этот общий метод для загруженных объектов модели.

public IEnumerable<Books> GetByFancy(int anInteger)
{
    DB db = Database.Current;
    var r = from b in db.Books
            join a in db.Authors on b.AuthorId equals a.AuthorId
            where a.AuthorId == anInteger
            select b;

    if (r.IsNull()) yield break;
    foreach (var m in r)
    {
        m.AcceptChanges();
        yield return m;
    }
}

Так что в приведенном выше примере есть общие повторяющиеся строки, где я хотел бы сделать общий вызов метода для удаления общих повторяющихся строк кода.


Вот исключение, которое я получаю.

System.InvalidCastException : Unable to cast object of type '<AcceptChangesAndYield>d__6' to type 'System.Collections.Generic.IEnumerable`1[MyModels.StatusLookup]'.

public IEnumerable<MyModels.StatusLookup> GetAll()
{
    var results = Database.Current.pStatusLookupLoadAll()
        .ExecuteTypedList<MyModels.StatusLookup>();
    return (IEnumerable<MyModels.StatusLookup>)results.AcceptChangesAndYield();
}

А вот метод расширения, с которым я пытался это сделать.

public static class BaseModelExtensions
{
    public static IEnumerable<MyModels.BaseModel> AcceptChanges(this IEnumerable<MyModels.BaseModel> obj)
    {
        if (obj.IsNull()) yield break;
        foreach (var m in obj)
        {
            m.AcceptChanges();
            yield return m;
        }
    }

    public static IEnumerable<MyModels.Interfaces.ILookup> AcceptChangesAndYield(this IEnumerable<MyModels.Interfaces.ILookup> obj)
    {
        if (obj.IsNull()) yield break;
        foreach (var m in obj)
        {
            yield return m;
        }
    }
}

1 Ответ

0 голосов
/ 06 апреля 2011

Обновление : Ваша проблема не связана конкретно с ключевым словом yield. Это связано с дисперсией типа .

Ваш метод AcceptChangesAndYield возвращает объект типа, реализующего IEnumerable<MyModels.Interfaces.ILookup> (на самом деле это тип, сгенерированный компилятором, но это не очень важно). В своем вызове метода вы пытаетесь уменьшить до IEnumerable<MyModels.StatusLookup>, что более конкретно .

Интерфейс IEnumerable<T> имеет ковариант , что позволит вам upcast до менее специфичного типа; например, вы можете привести от List<string> к IEnumerable<object> (в любом случае в .NET 4.0). Тип, сгенерированный компилятором для предоставления возвращаемого значения вашего метода AcceptChangesAndYield, реализует только IEnumerable<MyModels.Interfaces.ILookup>, поэтому вы можете преобразовать результат в IEnumerable<object> (например), но не в IEnumerable<MyModels.StatusLookup>.

К счастью, решение довольно простое. Переопределите ваш AcceptChangesAndYield метод следующим образом:

// Note: We are using a generic type constraint on T.
public static IEnumerable<T> AcceptChangesAndYield<T>(this IEnumerable<T> obj)
    where T : MyModels.Interfaces.ILookup
{
    if (obj.IsNull()) yield break;
    foreach (var m in obj)
    {
        // Did you mean to put m.AcceptChanges() here?
        yield return m;
    }
}

Это, в свою очередь, позволит реализовать ваш метод GetAll следующим образом:

public IEnumerable<MyModels.StatusLookup> GetAll()
{
    var results = Database.Current.pStatusLookupLoadAll()
        .ExecuteTypedList<MyModels.StatusLookup>();
    // Note: no need for a cast, as the return value is now
    // already strongly typed as IEnumerable<MyModels.StatusLookup>.
    return results.AcceptChangesAndYield();
}

Оригинальный ответ : Кажется, вы просто хотите этого?

IEnumerable<T> EnumerateResults<T>(IEnumerable<T> results)
{
    if (results.IsNull()) yield break;
    foreach (T result in results)
    {
        // ..Common logic lines...
        yield return result;
    }
}

Тогда в вашем коде, где вы хотите удалить дублирование, у вас будет просто:

// Specific stuff
var results = BlahBlahBlah();

// Common stuff
return EnumerateResults(results);

Правильно? Или я неправильно понимаю вашу проблему?

...